From 79ccfa90a141827f9911fa8e86dc7eb224a305be Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 10 Jul 2023 22:01:16 -0500 Subject: [PATCH 001/402] tests/util/grub-shell: Add --verbose to grub-mkrescue when $debug is greater than 2 Since this is fairly verbose output, do not enable first level of debug is turned on. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- tests/util/grub-shell.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/util/grub-shell.in b/tests/util/grub-shell.in index a0ca18e41..5cbe79447 100644 --- a/tests/util/grub-shell.in +++ b/tests/util/grub-shell.in @@ -422,7 +422,9 @@ fi if [ x$boot != xnet ] && [ x$boot != xemu ]; then pkgdatadir="${builddir}" \ - exec_show_error "${builddir}/grub-mkrescue" "--output=${isofile}" \ + exec_show_error "${builddir}/grub-mkrescue" \ + ${debug:+$([ "$debug" -gt 2 ] && echo -n "--verbose")} \ + "--output=${isofile}" \ "--override-directory=${builddir}/grub-core" \ --rom-directory="${rom_directory}" \ --locale-directory="${srcdir}/po" \ From 76e8962e8505296f623eb2a51747c32f993b4cf6 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 10 Jul 2023 22:01:17 -0500 Subject: [PATCH 002/402] tests/util/grub-shell: Allow setting default timeout via GRUB_SHELL_DEFAULT_TIMEOUT envvar Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- tests/util/grub-shell.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/util/grub-shell.in b/tests/util/grub-shell.in index 5cbe79447..9669b7227 100644 --- a/tests/util/grub-shell.in +++ b/tests/util/grub-shell.in @@ -225,7 +225,7 @@ case "${grub_modinfo_target_cpu}-${grub_modinfo_platform}" in console=console;; esac -timeout=60 +timeout=${GRUB_SHELL_DEFAULT_TIMEOUT:-60} mkimage_extra_arg= debug=${GRUB_SHELL_DEFAULT_DEBUG:-$GRUB_TEST_DEFAULT_DEBUG} From e1b97d7c37cd8896dfd0c08f59eae27984c6765f Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 10 Jul 2023 22:01:18 -0500 Subject: [PATCH 003/402] tests/util/grub-shell: Allow setting the value of debug regardless of its previous state This allows an invocation of grub-shell to set the value of debug regardless of the global default environment variable GRUB_SHELL_DEFAULT_DEBUG. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- tests/util/grub-shell.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/util/grub-shell.in b/tests/util/grub-shell.in index 9669b7227..e066105de 100644 --- a/tests/util/grub-shell.in +++ b/tests/util/grub-shell.in @@ -248,6 +248,8 @@ for option in "$@"; do trim=0 ;; --debug) debug=$((debug+1)) ;; + --debug=*) + debug=$((`echo "$option" | sed -e 's/--debug=//'`)) ;; --modules=*) ms=`echo "$option" | sed -e 's/--modules=//' -e 's/,/ /g'` modules="$modules $ms" ;; From 7f6149449ca41ee520c0d5d222379b2081d97076 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 10 Jul 2023 22:01:19 -0500 Subject: [PATCH 004/402] tests/util/grub-shell-luks-tester: Allow GRUB_SHELL_LUKS_DEFAULT_DEBUG and GRUB_TEST_DEFAULT_DEBUG to specify the debug level to grub-shell Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- tests/util/grub-shell-luks-tester.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/util/grub-shell-luks-tester.in b/tests/util/grub-shell-luks-tester.in index 18920619a..850a20d6a 100644 --- a/tests/util/grub-shell-luks-tester.in +++ b/tests/util/grub-shell-luks-tester.in @@ -37,6 +37,8 @@ keyfile_offset= keyfile_size= KEYFILE_SIZE_MAX=4096 +debug="${GRUB_SHELL_LUKS_DEFAULT_DEBUG:-$GRUB_TEST_DEFAULT_DEBUG}" + # Usage: usage # Print the usage. usage () { @@ -350,7 +352,7 @@ if [ -n "$detached_header" ]; then fi # Run the test in grub-shell -@builddir@/grub-shell ${debug:+--debug} $grub_shell_opts \ +@builddir@/grub-shell ${debug:+--debug=$debug} $grub_shell_opts \ --modules="$modules" --qemu-opts="$qemuopts" \ --files="${grub_testvars}=${testvars}" "$testcase" \ >$testoutput From e641b4ab32f1724005ec8bf58214df86de7e81d9 Mon Sep 17 00:00:00 2001 From: Oskari Pirhonen Date: Mon, 10 Jul 2023 23:55:43 -0500 Subject: [PATCH 005/402] util/grub.d/25_bli.in: Fix shebang on unmerged-usr On an unmerged-usr system, grub-mkconfig errors out with the following error due to /usr/bin/sh not existing: /usr/sbin/grub-mkconfig: /etc/grub.d/25_bli: /usr/bin/sh: bad interpreter: No such file or directory Use a /bin/sh shebang to fix the error as well as match the other existing files. Fixes: 158a6583e (util/grub.d/25_bli.in: Activate bli module on EFI) Signed-off-by: Oskari Pirhonen Reviewed-by: Glenn Washburn Reviewed-by: Daniel Kiper Reviewed-by: Oliver Steffen --- util/grub.d/25_bli.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/grub.d/25_bli.in b/util/grub.d/25_bli.in index 6e4538716..26e27a019 100644 --- a/util/grub.d/25_bli.in +++ b/util/grub.d/25_bli.in @@ -1,4 +1,4 @@ -#!/usr/bin/sh +#! /bin/sh set -e # grub-mkconfig helper script. From 459f028cd36555b117e3c0d5ae952190b7968c19 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Tue, 11 Jul 2023 12:07:29 -0500 Subject: [PATCH 006/402] docs: Add missing assumption Also reword a prior sentence to be more clear. Fixes: 5a3d2b4742df (docs: Add debugging chapter to development documentation) Signed-off-by: Glenn Washburn Reviewed-by: Oskari Pirhonen Reviewed-by: Daniel Kiper --- docs/grub-dev.texi | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi index 0834ca562..a695b02f0 100644 --- a/docs/grub-dev.texi +++ b/docs/grub-dev.texi @@ -660,10 +660,11 @@ GDB is started from the @file{grub-core} directory in the GRUB2 build directory. GRUB2 developers have made this more simple by including a GDB script which does much of the setup. This file is at @file{grub-core/gdb_grub} in the build directory and is also installed via @command{make install}. -If not building GRUB, the distribution may have a package which installs +When using a pre-built GRUB, the distribution may have a package which installs this GDB script along with debug symbol binaries, such as Debian's @samp{grub-pc-dbg} package. The GDB script is intended to be used -like so, assuming: +like so, assuming that @samp{/path/to/script} is the path to the directory +containing the gdb_grub script and debug symbol files: @example cd $(dirname /path/to/script/gdb_grub) From eba6bdcd3ad938a5f189a56f06e20150ed3d084a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Grzelak?= Date: Wed, 19 Jul 2023 00:48:21 +0200 Subject: [PATCH 007/402] configure: Fix SDL2 typo by referencing value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During configuration of SDL2, variable enable_grub_emu_sdl2 is checked whether to throw an error message. However, error could not happen because two unequal strings were compared. Fix this by referencing value of enable_grub_emu_sdl2, not name. Fixes: 17d6ac1a7 (emu: Add SDL2 support) Signed-off-by: MichaƂ Grzelak Reviewed-by: Julian Andres Klode Reviewed-by: Daniel Kiper Reviewed-by: Paul Menzel --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 01500ffdf..27c5918fa 100644 --- a/configure.ac +++ b/configure.ac @@ -1609,7 +1609,7 @@ if test "$platform" = emu; then AC_SUBST(HAVE_SDL2)], [grub_emu_sdl2_excuse="libSDL2 libraries are required to build \`grub-emu' with SDL2 support"]) [fi] - if test x"enable_grub_emu_sdl2" = xyes && test x"$grub_emu_sdl2_excuse" != x ; then + if test x"$enable_grub_emu_sdl2" = xyes && test x"$grub_emu_sdl2_excuse" != x ; then AC_MSG_ERROR([SDL2 support for grub-emu was explicitly requested but can't be compiled ($grub_emu_sdl2_excuse)]) fi if test x"$grub_emu_sdl2_excuse" = x ; then From 3077b39baef99afe534b582b9024bba877786e40 Mon Sep 17 00:00:00 2001 From: Qiumiao Zhang Date: Tue, 25 Jul 2023 11:18:59 +0800 Subject: [PATCH 008/402] util/grub-mount: Fix memory leak in fuse_getattr() Signed-off-by: Qiumiao Zhang Reviewed-by: Daniel Kiper --- util/grub-mount.c | 1 + 1 file changed, 1 insertion(+) diff --git a/util/grub-mount.c b/util/grub-mount.c index 1c35b6a6e..c69889df9 100644 --- a/util/grub-mount.c +++ b/util/grub-mount.c @@ -198,6 +198,7 @@ fuse_getattr (const char *path, struct stat *st, (fs->fs_dir) (dev, path2, fuse_getattr_find_file, &ctx); grub_free (path2); + free (pathname); if (!ctx.file_exists) { grub_errno = GRUB_ERR_NONE; From 2e1279af2bf11d48381bc26a368b39f3961cd5ab Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Wed, 26 Jul 2023 13:47:24 -0500 Subject: [PATCH 009/402] docs: Group usage of user-space utilities into single chapter Signed-off-by: Glenn Washburn Reviewed-by: Oskari Pirhonen Reviewed-by: Daniel Kiper --- docs/grub.texi | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index b81267980..9d5daa6ee 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -104,15 +104,7 @@ This edition documents version @value{VERSION}. * Platform-specific operations:: Platform-specific operations * Supported kernels:: Supported kernels * Troubleshooting:: Error messages produced by GRUB -* Invoking grub-install:: How to use the GRUB installer -* Invoking grub-mkconfig:: Generate a GRUB configuration file -* Invoking grub-mkpasswd-pbkdf2:: - Generate GRUB password hashes -* Invoking grub-mkrelpath:: Make system path relative to its root -* Invoking grub-mkrescue:: Make a GRUB rescue image -* Invoking grub-mount:: Mount a file system using GRUB -* Invoking grub-probe:: Probe device information for GRUB -* Invoking grub-script-check:: Check GRUB script file for syntax errors +* User-space utilities:: Usage of user-space utilities * Obtaining and Building GRUB:: How to obtain and build GRUB * Reporting bugs:: Where you should send a bug report * Future:: Some future plans on GRUB @@ -6995,8 +6987,24 @@ entry which claims partition start at block 0. This change will not hamper bootability on other machines. +@node User-space utilities +@chapter User-space utilities + +@menu +* Invoking grub-install:: How to use the GRUB installer +* Invoking grub-mkconfig:: Generate a GRUB configuration file +* Invoking grub-mkpasswd-pbkdf2:: + Generate GRUB password hashes +* Invoking grub-mkrelpath:: Make system path relative to its root +* Invoking grub-mkrescue:: Make a GRUB rescue image +* Invoking grub-mount:: Mount a file system using GRUB +* Invoking grub-probe:: Probe device information for GRUB +* Invoking grub-script-check:: Check GRUB script file for syntax errors +@end menu + + @node Invoking grub-install -@chapter Invoking grub-install +@section Invoking grub-install The program @command{grub-install} generates a GRUB core image using @command{grub-mkimage} and installs it on your system. You must specify the @@ -7062,7 +7070,7 @@ outside of the MBR. Disable the Reed-Solomon codes with this option. @end table @node Invoking grub-mkconfig -@chapter Invoking grub-mkconfig +@section Invoking grub-mkconfig The program @command{grub-mkconfig} generates a configuration file for GRUB (@pxref{Simple configuration}). @@ -7088,7 +7096,7 @@ it to standard output. @node Invoking grub-mkpasswd-pbkdf2 -@chapter Invoking grub-mkpasswd-pbkdf2 +@section Invoking grub-mkpasswd-pbkdf2 The program @command{grub-mkpasswd-pbkdf2} generates password hashes for GRUB (@pxref{Security}). @@ -7116,7 +7124,7 @@ Length of the salt. Defaults to 64. @node Invoking grub-mkrelpath -@chapter Invoking grub-mkrelpath +@section Invoking grub-mkrelpath The program @command{grub-mkrelpath} makes a file system path relative to the root of its containing file system. For instance, if @file{/usr} is a @@ -7143,7 +7151,7 @@ Print the version number of GRUB and exit. @node Invoking grub-mkrescue -@chapter Invoking grub-mkrescue +@section Invoking grub-mkrescue The program @command{grub-mkrescue} generates a bootable GRUB rescue image (@pxref{Making a GRUB bootable CD-ROM}). @@ -7201,7 +7209,7 @@ built-in default. @node Invoking grub-mount -@chapter Invoking grub-mount +@section Invoking grub-mount The program @command{grub-mount} performs a read-only mount of any file system or file system image that GRUB understands, using GRUB's file system @@ -7288,7 +7296,7 @@ Print verbose messages. @node Invoking grub-probe -@chapter Invoking grub-probe +@section Invoking grub-probe The program @command{grub-probe} probes device information for a given path or device. @@ -7374,7 +7382,7 @@ Print verbose messages. @node Invoking grub-script-check -@chapter Invoking grub-script-check +@section Invoking grub-script-check The program @command{grub-script-check} takes a GRUB script file (@pxref{Shell-like scripting}) and checks it for syntax errors, similar to From 7ff4f3c9632fa635d21a7afdd38b4bcfbfae2c99 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Wed, 26 Jul 2023 13:50:11 -0500 Subject: [PATCH 010/402] docs: Document hexdump command Signed-off-by: Glenn Washburn Reviewed-by: Oskari Pirhonen Reviewed-by: Daniel Kiper --- docs/grub.texi | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/grub.texi b/docs/grub.texi index 9d5daa6ee..3634507ac 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -4327,6 +4327,7 @@ you forget a command, you can run the command @command{help} * halt:: Shut down your computer * hashsum:: Compute or check hash checksum * help:: Show help messages +* hexdump:: Show raw contents of a file or memory * insmod:: Insert a module * keystatus:: Check key modifier status * list_env:: List variables in environment block @@ -4945,6 +4946,20 @@ about each of the commands whose names begin with those @var{patterns}. @end deffn +@node hexdump +@subsection hexdump + +@deffn Command hexdump [--skip offset] [--length len] FILE_OR_DEVICE +Show raw contents of a file or memory. When option @option{--skip} is given, +@samp{offset} number of bytes are skipped from the start of the device or +file given. And @option{--length} allows specifying a maximum number of bytes +to be shown. + +If given the special device named @samp{(mem)}, then the @samp{offset} given to +@option{--skip} is treated as the address of a memory location to dump from. +@end deffn + + @node insmod @subsection insmod @@ -6131,7 +6146,6 @@ GRUB shell may provide more information on parameters and usage. @item @command{gdbstub} - Start GDB stub on given port @item @command{gdbstub_stop} - Stop GDB stub @item @command{hdparm} - Get/set ATA disk parameters. -@item @command{hexdump} - Show raw contents of a file or memory. @item @command{hexdump_random} - Hexdump random data. @item @command{inb} - Read 8-bit value from PORT. @item @command{inl} - Read 32-bit value from PORT. From 711e034235987f5642ee00841f7c620d2979210f Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Wed, 26 Jul 2023 13:50:12 -0500 Subject: [PATCH 011/402] docs: A note to cat that hexdump should be used for binary data The cat command should not be used to print binary data because it can show bytes not in the binary data and not show bytes that are in the data, which can lead to confusion. This happens because cat does some processing of the data stream, namely trying to decode substrings as UTF-8. Signed-off-by: Glenn Washburn Reviewed-by: Oskari Pirhonen Reviewed-by: Daniel Kiper --- docs/grub.texi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/grub.texi b/docs/grub.texi index 3634507ac..b233e28e0 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -4507,6 +4507,12 @@ will be displayed as a simple new line. Otherwise, the carriage return will be displayed as a control character (@samp{}) to make it easier to see when boot problems are caused by a file formatted using DOS-style line endings. + +Note: @command{cat} can be used to view the contents of devices using the +block list syntax (@pxref{Block list syntax}). However, it is not advised +to view binary data because it will try to decode UTF-8 strings, which can +lead to some bytes missing or added in the output. Instead, use the +@command{hexdump} command (@pxref{hexdump}). @end deffn From b0890d371ca1e49877c45d696deb9102f1b605c3 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Wed, 26 Jul 2023 16:02:36 -0500 Subject: [PATCH 012/402] term/ns8250-spcr: Continue processing SPCR table even if revision is < 2 According to commit 0231d00082 (ACPI: SPCR: Make SPCR available to x86) to the Linux kernel, "On x86, many systems have a valid SPCR table but the table version is not 2 so the table version check must be a warning." Signed-off-by: Glenn Washburn Reviewed-by: Benjamin Herrenschmidt Reviewed-by: Daniel Kiper --- grub-core/term/ns8250-spcr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/grub-core/term/ns8250-spcr.c b/grub-core/term/ns8250-spcr.c index d101bffb5..d52b52c26 100644 --- a/grub-core/term/ns8250-spcr.c +++ b/grub-core/term/ns8250-spcr.c @@ -18,6 +18,7 @@ #if !defined(GRUB_MACHINE_IEEE1275) && !defined(GRUB_MACHINE_QEMU) +#include #include #include #include @@ -34,7 +35,8 @@ grub_ns8250_spcr_init (void) if (spcr == NULL) return NULL; if (spcr->hdr.revision < 2) - return NULL; + grub_dprintf ("serial", "SPCR table revision %d < 2, continuing anyway\n", + (int) spcr->hdr.revision); if (spcr->intf_type != GRUB_ACPI_SPCR_INTF_TYPE_16550 && spcr->intf_type != GRUB_ACPI_SPCR_INTF_TYPE_16550X) return NULL; From 43bf0b2a10bc8f312ad4b38649c8523620d66d2b Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Tue, 1 Aug 2023 19:58:51 -0500 Subject: [PATCH 013/402] docs: Improve initrd documentation A list of improvements: * Remove reference to "initial ramdisk" and replace with "initrd". This then covers the case of ramdisk and ramfs, which is the usual method with kernels 2.6 and newer. * Add sentence with URL to initrd documentation Linux kernel. * Add a section documenting how to have the initrd command generate a new-style initrd via a specially crafted argument and include an example. * Update initrd16 to refer to the initrd section and make note that initrd16 is only on the pc platform. Signed-off-by: Glenn Washburn Reviewed-by: Oskari Pirhonen Reviewed-by: Paul Menzel Reviewed-by: Daniel Kiper --- docs/grub.texi | 48 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index b233e28e0..be2261670 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -4068,10 +4068,43 @@ load a defective boot loader, such as SCO UnixWare 7.1. @subsection initrd @deffn Command initrd file [file @dots{}] -Load, in order, all initial ramdisks for a Linux kernel image, and set -the appropriate parameters in the Linux setup area in memory. This may only -be used after the @command{linux} command (@pxref{linux}) has been run. See -also @ref{GNU/Linux}. +Load, in order, all initrds for a Linux kernel image, and set the +appropriate parameters in the Linux setup area in memory. This may only +be used after the @command{linux} command (@pxref{linux}) has been run. +See @ref{GNU/Linux} for more info on booting GNU/Linux. For more +information on initrds see the GNU/Linux kernel +@uref{https://docs.kernel.org/filesystems/ramfs-rootfs-initramfs.html, +documentation}. + +A new-style initrd (for kernels newer than 2.6) containing one file +with leading path components can also be generated at run time. This +can be done by prefixing an argument with @code{newc:} followed by the +path of the file in the new initrd, a @code{:}, and then the GRUB file +path to the file data to be be included. + +For example: +@example +initrd newc:/etc/ssh/config:(hd0,2)/home/user/.ssh/config \ + newc:/etc/ssh/ssh_host_rsa_key:/etc/ssh/ssh_host_rsa_key \ + /boot/initrd.gz \ + newc:/init:/home/user/init.fixed +@end example + +This command will generate two new-style initrds on the fly. The first +contains the path @samp{/etc/ssh/config} with the contents of +@samp{(hd0,2)/home/user/.ssh/config} and the path +@samp{/etc/ssh/ssh_host_rsa_key} with the contents of +@samp{/etc/ssh/ssh_host_rsa_key} on the @var{root} device. Parent directory +paths will automatically be generated as needed. This first generated initrd +will then have @samp{/boot/initrd.gz} concatenated after it. Next, another +new-style archive will be generated with the contents of @samp{/home/user/init.fixed} +in the path @samp{/init} and appended to the previous concatenation. Finally, +the result will be sent to the kernel when booted. + +Keep in mind that paths that come later will take precedence. So in the +example above, the generated path @samp{/init} will overwrite any @samp{/init} +in @samp{/boot/initrd.gz}. This can be useful when changing the main initrd +is undesirable or difficult. @end deffn @@ -4079,12 +4112,13 @@ also @ref{GNU/Linux}. @subsection initrd16 @deffn Command initrd16 file [file @dots{}] -Load, in order, all initial ramdisks for a Linux kernel image to be booted in +Load, in order, all initrds for a Linux kernel image to be booted in 16-bit mode, and set the appropriate parameters in the Linux setup area in memory. This may only be used after the @command{linux16} command -(@pxref{linux16}) has been run. See also @ref{GNU/Linux}. +(@pxref{linux16}) has been run. See also @ref{GNU/Linux} and the @command{initrd} +command (@pxref{initrd}) for more details on arguments. -This command is only available on x86 systems. +This command is only available on the pc platform for x86 systems. @end deffn From 1de58326994b0045ccfb28017d05fc1ddecdb92d Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Fri, 11 Aug 2023 16:30:04 -0500 Subject: [PATCH 014/402] commands/videoinfo: Prevent crash when run while video driver already active The videoinfo command will initialize all non-active video adapters. Video drivers tend to zero out the global framebuffer object on initialization. This is not a problem when there is no active video adapter. However, when there is, then outputting to the video adapter will cause a crash because methods in the framebuffer object are reinitialized. For example, this command sequence will cause a crash. terminal_output --append gfxterm; videoinfo When running in a QEMU headless with GRUB built for the x86_64-efi target, the first command initializes the Bochs video adapter, which, among other things, sets the set_page() member function. Then when videoinfo is run, all non-Bochs video adapters will be initialized, each one wiping the framebuffer and thus setting set_page to NULL. Soon after the videoinfo command finishes there will be a call to grub_refresh(), which will ultimately call the framebuffer's set_page which will be NULL and cause a crash when called. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- grub-core/commands/videoinfo.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/grub-core/commands/videoinfo.c b/grub-core/commands/videoinfo.c index 5eb969748..205ba78c9 100644 --- a/grub-core/commands/videoinfo.c +++ b/grub-core/commands/videoinfo.c @@ -191,6 +191,11 @@ grub_cmd_videoinfo (grub_command_t cmd __attribute__ ((unused)), /* Don't worry about errors. */ grub_errno = GRUB_ERR_NONE; } + else if (id != GRUB_VIDEO_DRIVER_NONE) + { + grub_puts_ (N_(" A video driver is active, cannot initialize this driver until it is deactivated\n")); + continue; + } else { if (adapter->init ()) From fcbea18c584257adc824cbb1c66cec2f4329869c Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 14 Aug 2023 01:03:41 -0500 Subject: [PATCH 015/402] fs/archelp: If path given to grub_archelp_dir() is not a directory return error Specifically, return GRUB_ERR_BAD_FILE_TYPE because this is what is expected by the ls command when it is given a path to a non-directory. This fixes a bug where calling ls with a list of non-directory paths outputs a blank line for each such argument. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- grub-core/fs/archelp.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/grub-core/fs/archelp.c b/grub-core/fs/archelp.c index 998de88b8..c1dcc6285 100644 --- a/grub-core/fs/archelp.c +++ b/grub-core/fs/archelp.c @@ -180,6 +180,14 @@ grub_archelp_dir (struct grub_archelp_data *data, if (p) *p = 0; + if ((*n == 0) && ((mode & GRUB_ARCHELP_ATTR_TYPE) + != GRUB_ARCHELP_ATTR_DIR)) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory")); + grub_free (name); + goto fail; + } + if (((!prev) || (grub_strcmp (prev, name) != 0)) && *n != 0) { struct grub_dirhook_info info; From 6889c67fe7f2d6bbaa1f6050d9e29e616740936b Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 14 Aug 2023 01:16:53 -0500 Subject: [PATCH 016/402] commands/ls: Send correct dirname to print functions For each non-directory path argument to the ls command, the full path was being sent to the print functions, instead of the dirname. The long output print function expected dirname to be the directory containing the file and so could not open the file to get the file size because the generated path was incorrect. This caused the output to be a blank line. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- grub-core/commands/ls.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/grub-core/commands/ls.c b/grub-core/commands/ls.c index 8e98c73cc..c746eae0c 100644 --- a/grub-core/commands/ls.c +++ b/grub-core/commands/ls.c @@ -242,8 +242,8 @@ grub_ls_list_files (char *dirname, int longlist, int all, int human) grub_file_close (file); p = grub_strrchr (dirname, '/') + 1; - dirname = grub_strndup (dirname, p - dirname); - if (! dirname) + ctx.dirname = grub_strndup (dirname, p - dirname); + if (ctx.dirname == NULL) goto fail; all = 1; @@ -253,7 +253,7 @@ grub_ls_list_files (char *dirname, int longlist, int all, int human) else print_files (p, &info, &ctx); - grub_free (dirname); + grub_free (ctx.dirname); } if (grub_errno == GRUB_ERR_NONE) From 4fdcb339bbcfbf5c234c764c83813ab8de9c9657 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 14 Aug 2023 01:20:29 -0500 Subject: [PATCH 017/402] commands/ls: Print "????????????" if unable to get file size In long list mode, if the file can not be opened, the file is not printed. Instead, print the file but print the size as "????????????". Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- grub-core/commands/ls.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/grub-core/commands/ls.c b/grub-core/commands/ls.c index c746eae0c..6a1c7f5d3 100644 --- a/grub-core/commands/ls.c +++ b/grub-core/commands/ls.c @@ -131,20 +131,20 @@ print_files_long (const char *filename, const struct grub_dirhook_info *info, should be reported as directories. */ file = grub_file_open (pathname, GRUB_FILE_TYPE_GET_SIZE | GRUB_FILE_TYPE_NO_DECOMPRESS); - if (! file) + if (file) { - grub_errno = 0; - grub_free (pathname); - return 0; - } - - if (! ctx->human) - grub_printf ("%-12llu", (unsigned long long) file->size); - else - grub_printf ("%-12s", grub_get_human_size (file->size, + if (! ctx->human) + grub_printf ("%-12llu", (unsigned long long) file->size); + else + grub_printf ("%-12s", grub_get_human_size (file->size, GRUB_HUMAN_SIZE_SHORT)); - grub_file_close (file); + grub_file_close (file); + } + else + grub_xputs ("????????????"); + grub_free (pathname); + grub_errno = GRUB_ERR_NONE; } else grub_printf ("%-12s", _("DIR")); From a63ef8c78c80561ca87802769553afaa2dcc41b4 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 14 Aug 2023 14:59:00 -0500 Subject: [PATCH 018/402] tests/util/grub-shell: Convert spaces to TABs Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- tests/util/grub-shell.in | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/util/grub-shell.in b/tests/util/grub-shell.in index e066105de..29c7e936e 100644 --- a/tests/util/grub-shell.in +++ b/tests/util/grub-shell.in @@ -66,8 +66,8 @@ exec_show_error () { v=`$@ 2>&1` ret=$? if [ "$ret" != 0 ]; then - echo "$v" >&2 - exit $ret + echo "$v" >&2 + exit $ret fi } @@ -247,7 +247,7 @@ for option in "$@"; do --no-trim) trim=0 ;; --debug) - debug=$((debug+1)) ;; + debug=$((debug+1)) ;; --debug=*) debug=$((`echo "$option" | sed -e 's/--debug=//'`)) ;; --modules=*) @@ -270,10 +270,10 @@ for option in "$@"; do pseries=y ;; --qemu-opts=*) - qs=`echo "$option" | sed -e 's/--qemu-opts=//'` - qemuopts="$qemuopts $qs" ;; + qs=`echo "$option" | sed -e 's/--qemu-opts=//'` + qemuopts="$qemuopts $qs" ;; --disk=*) - dsk=`echo "$option" | sed -e 's/--disk=//'` + dsk=`echo "$option" | sed -e 's/--disk=//'` if [ ${grub_modinfo_platform} = emu ]; then echo "(hd$disk) $dsk" >> "$device_map" disk="$((disk+1))" @@ -282,7 +282,7 @@ for option in "$@"; do echo "Too many disks" 1>&2 exit 1; fi - qemuopts="$qemuopts -$disk$dsk" + qemuopts="$qemuopts -$disk$dsk" if [ "$disk" = "hda " ]; then disk="hdb "; elif [ "$disk" = "hdb " ]; then @@ -295,7 +295,7 @@ for option in "$@"; do fi ;; --timeout=*) - timeout=`echo "$option" | sed -e 's/--timeout=//'` + timeout=`echo "$option" | sed -e 's/--timeout=//'` ;; # Intentionally undocumented @@ -305,7 +305,7 @@ for option in "$@"; do mkimage_extra_arg="$mkimage_extra_arg `echo "$option" | sed 's/--grub-mkimage-extra=//'`" ;; --boot=*) - dev=`echo "$option" | sed -e 's/--boot=//'` + dev=`echo "$option" | sed -e 's/--boot=//'` if [ "$dev" = "fd" ] ; then boot=fd; elif [ "$dev" = "hd" ] ; then boot=hd; elif [ "$dev" = "cd" ] ; then boot=cd; From ee2349ef655399970f2a4521e3e35f17d81ffe14 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 14 Aug 2023 15:00:10 -0500 Subject: [PATCH 019/402] tests/util/grub-shell-luks-tester: Do not remove generated files when test fails to allow debugging Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- tests/util/grub-shell-luks-tester.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/util/grub-shell-luks-tester.in b/tests/util/grub-shell-luks-tester.in index 850a20d6a..3f9d1be17 100644 --- a/tests/util/grub-shell-luks-tester.in +++ b/tests/util/grub-shell-luks-tester.in @@ -145,7 +145,9 @@ cleanup() { if [ -e "$luksdev" ]; then cryptsetup close "$luksdev" fi - [ -z "$debug" ] && rm -rf "$lukstestdir" || : + if [ -z "$debug" ] && [ "${RET:-1}" -eq 0 ]; then + rm -rf "$lukstestdir" || : + fi } trap cleanup EXIT INT TERM KILL QUIT From 846829aca8ce3f89618e20af1abe4b84958b3fe7 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 14 Aug 2023 22:33:51 -0500 Subject: [PATCH 020/402] tests/util/grub-shell: Allow explicitly using other serial ports for output While here, move "-qemu=*" case to be next to the "--qemu-opts=*" case. This causes no change in logic, but is more logically located. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- tests/util/grub-shell.in | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/util/grub-shell.in b/tests/util/grub-shell.in index 29c7e936e..715e1c2e6 100644 --- a/tests/util/grub-shell.in +++ b/tests/util/grub-shell.in @@ -259,8 +259,6 @@ for option in "$@"; do --mkrescue-arg=*) mkr=`echo "$option" | sed -e 's/--mkrescue-arg=//' -e 's/,/ /g'` mkrescue_args="$mkrescue_args $mkr" ;; - --qemu=*) - qemu=`echo "$option" | sed -e 's/--qemu=//' -e 's/,/ /g'`;; --pseries) qemu=qemu-system-ppc64 serial_port=ieee1275/hvterm @@ -269,6 +267,10 @@ for option in "$@"; do trim=1 pseries=y ;; + --serial=*) + serial_port=`echo "$option" | sed -e 's/--serial=//'`;; + --qemu=*) + qemu=`echo "$option" | sed -e 's/--qemu=//' -e 's/,/ /g'`;; --qemu-opts=*) qs=`echo "$option" | sed -e 's/--qemu-opts=//'` qemuopts="$qemuopts $qs" ;; From 6d6b95720c5ef39f508693181df1e844df1c0ca0 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 14 Aug 2023 22:33:52 -0500 Subject: [PATCH 021/402] tests: Add serial_test This test is meant to test output via various serial devices. Currently, only the PCI serial device is tested. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- Makefile.util.def | 6 +++++ tests/serial_test.in | 55 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 tests/serial_test.in diff --git a/Makefile.util.def b/Makefile.util.def index 1e9a13d3e..6fe08efd8 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -1130,6 +1130,12 @@ script = { common = tests/netboot_test.in; }; +script = { + testcase = nonnative; + name = serial_test; + common = tests/serial_test.in; +}; + script = { testcase = nonnative; name = pseries_test; diff --git a/tests/serial_test.in b/tests/serial_test.in new file mode 100644 index 000000000..48655d4b9 --- /dev/null +++ b/tests/serial_test.in @@ -0,0 +1,55 @@ +#! @BUILD_SHEBANG@ +# Copyright (C) 2023 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 . + +set -e +grubshell=@builddir@/grub-shell + +. "@builddir@/grub-core/modinfo.sh" + +case "${grub_modinfo_target_cpu}-${grub_modinfo_platform}" in + # PLATFORM: emu is different. + *-emu) + exit 77;; + # PLATFORM: Flash targets. + i386-qemu | i386-coreboot | mips-qemu_mips | mipsel-qemu_mips) + exit 77;; + # FIXME: Currently grub-shell uses only -kernel for loongson. + mipsel-loongson) + exit 77;; + # FIXME: How do we setup serial ports in QEMU for these platforms? + *-ieee1275) + exit 77;; +esac + +# Figure out the PCI device ID that the serial device will be on. +output=$(echo terminal_output | "${grubshell}" --qemu-opts="-device pci-serial") +PCIID=$(echo "$output" | grep -o "serial_pci,..:..\.." | cut -d, -f2) + +if [ -z "$PCIID" ]; then + echo "Failed to find QEMU serial device." >&2 + echo "${output}" >&2 + exit 99 +fi + +# Test serial output via an emulated PCI serial card. +output=$(echo "terminal_output; hello" | \ + "${grubshell}" --serial="pci,${PCIID}" \ + --qemu-opts="-chardev file,path=/dev/stdout,id=ser1 -device pci-serial,chardev=ser1") + +v=$(echo -e "$output" | tail -n1) +if [ "$v" != "Hello World" ]; then + exit 1 +fi From 14c95e57fddb6c826bee7755232de62efc8eb45b Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Fri, 18 Aug 2023 12:15:27 -0500 Subject: [PATCH 022/402] kern/misc: Make grub_vsnprintf() C99/POSIX conformant To comply with C99 and POSIX standards, snprintf() should return the number of bytes that would be written to the string (excluding the terminating NUL byte) if the buffer size was big enough. Before this change, the return value was the minimum of the standard return and the length of the buffer. Rarely is the return value of grub_snprintf() or grub_vsnprintf() used with current code, and the few places where it is used do not need to be changed. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- grub-core/kern/misc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c index 2890aad49..b57249acb 100644 --- a/grub-core/kern/misc.c +++ b/grub-core/kern/misc.c @@ -1201,7 +1201,7 @@ grub_vsnprintf (char *str, grub_size_t n, const char *fmt, va_list ap) free_printf_args (&args); - return ret < n ? ret : n; + return ret; } int From 296d3ec835ed6e3b90d740e497bb534f14fe4b79 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Fri, 18 Aug 2023 12:27:22 -0500 Subject: [PATCH 023/402] disk/cryptodisk: Fix missing change when updating to use grub_uuidcasecmp() This was causing the cryptomount command to return failure even though the crypto device was successfully added. Of course, this meant that any script using the return code would behave unexpectedly. Fixes: 3cf2e848bc03 (disk/cryptodisk: Allows UUIDs to be compared in a dash-insensitive manner) Suggested-by: Olaf Hering Signed-off-by: Glenn Washburn Reviewed-by: Patrich Steinhardt Reviewed-by: Daniel Kiper --- grub-core/disk/cryptodisk.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index 34b67a705..fb807e2a6 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -1236,7 +1236,8 @@ grub_cryptodisk_scan_device (const char *name, dev = grub_cryptodisk_scan_device_real (name, source, cargs); if (dev) { - ret = (cargs->search_uuid != NULL && grub_strcasecmp (cargs->search_uuid, dev->uuid) == 0); + ret = (cargs->search_uuid != NULL + && grub_uuidcasecmp (cargs->search_uuid, dev->uuid, sizeof (dev->uuid)) == 0); goto cleanup; } From fff2e9ee833ed640d62672110da7341593769e33 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Sat, 19 Aug 2023 01:59:17 -0500 Subject: [PATCH 024/402] tests/util/grub-shell-luks-tester: Allow setting timeout Allow using the envvar GRUB_SHELL_LUKS_TIMEOUT to change the default timeout. If not specified, use value of GRUB_SHELL_DEFAULT_TIMEOUT. And if that is not specified, fallback to original 600s timeout. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- tests/util/grub-shell-luks-tester.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/util/grub-shell-luks-tester.in b/tests/util/grub-shell-luks-tester.in index 3f9d1be17..8926cbc96 100644 --- a/tests/util/grub-shell-luks-tester.in +++ b/tests/util/grub-shell-luks-tester.in @@ -38,6 +38,7 @@ keyfile_size= KEYFILE_SIZE_MAX=4096 debug="${GRUB_SHELL_LUKS_DEFAULT_DEBUG:-$GRUB_TEST_DEFAULT_DEBUG}" +GRUB_SHELL_LUKS_TIMEOUT=${GRUB_SHELL_LUKS_TIMEOUT:-${GRUB_SHELL_DEFAULT_TIMEOUT:-600s}} # Usage: usage # Print the usage. @@ -134,7 +135,7 @@ done [ "${debug:-0}" -gt 1 ] && set -x -grub_shell_opts="$grub_shell_opts --timeout=600s" +grub_shell_opts="$grub_shell_opts --timeout=${GRUB_SHELL_LUKS_TIMEOUT}" if [ "${debug:-0}" -gt 2 ]; then grub_shell_opts="$grub_shell_opts --qemu-opts=-nographic" From 3c42272105ad307bfe6eeaacc4ee9d98be00537f Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Tue, 22 Aug 2023 13:26:28 -0500 Subject: [PATCH 025/402] docs: Use @ref instead of @xref The @xref command is meant to be used at the beginning of a sentence because its expansion creates a "See " prefix on all output formats, and on older makeinfo versions is strict about enforcing a "." or "," after the command. The @ref command has no such restriction and is just the link, which allows more control over output. This also fixes an issue where there was a repeated "see" in the output. Reported-by: Olaf Hering Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper Tested-by: Olaf Hering --- docs/grub.texi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/grub.texi b/docs/grub.texi index be2261670..975e521d1 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -956,7 +956,7 @@ information. @subsection Kexec with grub2-emu GRUB can be run in userspace by invoking the grub2-emu tool. It will -read all configuration scripts as if booting directly (see @xref{Loading +read all configuration scripts as if booting directly (see @ref{Loading an operating system directly}). With the @code{--kexec} flag, and kexec(8) support from the operating system, the @command{linux} command will directly boot the target image. For systems that lack working From ac8ae924ca024b4b56555e8fc032ba02dc27383b Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Tue, 22 Aug 2023 13:26:29 -0500 Subject: [PATCH 026/402] docs: Add menu to prevent older makeinfo versions from failing It has been reported that makeinfo version 4.13a complains and returns error when menus for chapter structuring commands are not present. It is also known that newer makeinfos, such as version 6.7, will create default menus when needed. Since the menu will be created regardless, explicitly create it to support older makeinfo versions. This also enables building to be successful when an older makeinfo is installed because in that case info files are attempted to be generated with the "all" target. Reported-by: Olaf Hering Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper Tested-by: Olaf Hering --- docs/grub-dev.texi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi index a695b02f0..1276c5930 100644 --- a/docs/grub-dev.texi +++ b/docs/grub-dev.texi @@ -788,6 +788,11 @@ out of its way to avoid using hardware breakpoints internally and uses them as briefly as possible when needed, thus allowing the user to have a maximal number at their disposal. +@menu +* OVMF debug log:: +* Using the gdbinfo command:: +@end menu + @node OVMF debug log @subsection OVMF debug log From 34a3adff822a6d136430e631d0a93448b27fac4e Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Mon, 28 Aug 2023 23:50:09 +0200 Subject: [PATCH 027/402] video/efi_gop: Require shadow if PixelBltOnly If the EFI graphics pixel format is PixelBltOnly, we cannot write directly to the frame buffer. We need the shadow frame buffer which we copy via the BitBlt operation to the hardware. If the pixel format is PixelBltOnly and allocation of the shadow frame buffer fails, we must raise an error to signal that the EFI GOP protocol is not usable. Signed-off-by: Heinrich Schuchardt Reviewed-by: Daniel Kiper --- grub-core/video/efi_gop.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/grub-core/video/efi_gop.c b/grub-core/video/efi_gop.c index 7247aeea7..9452f5e58 100644 --- a/grub-core/video/efi_gop.c +++ b/grub-core/video/efi_gop.c @@ -480,6 +480,10 @@ grub_video_gop_setup (unsigned int width, unsigned int height, if (!buffer) { grub_dprintf ("video", "GOP: couldn't allocate shadow\n"); + + if (info->pixel_format == GRUB_EFI_GOT_BLT_ONLY) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + grub_errno = 0; grub_video_gop_fill_mode_info (gop->mode->mode, info, &framebuffer.mode_info); From 42a831d7462ec3a114156d56ef8a03e1d47f19e7 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Mon, 3 Jul 2023 19:41:27 +0200 Subject: [PATCH 028/402] ZFS: support inode type embed into its ID This is a speedup used in some ZFS version. This trips GRUB and makes it unable to access directories. Just skip it for now and revisit if we ever need this speedup. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/fs/zfs/zfs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index 0e195db97..d6a321f08 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -73,6 +73,8 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define DATA_TYPE_NVLIST 19 #define DATA_TYPE_NVLIST_ARRAY 20 +#define DNODE_NUM_MASK 0xffffffffffffULL + #ifndef GRUB_UTIL static grub_dl_t my_mod; #endif @@ -2669,6 +2671,8 @@ dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type, grub_err_t err; grub_zfs_endian_t endian; + objnum &= DNODE_NUM_MASK; + blksz = grub_zfs_to_cpu16 (mdn->dn.dn_datablkszsec, mdn->endian) << SPA_MINBLOCKSHIFT; epbs = zfs_log2 (blksz) - DNODE_SHIFT; From 96446ce14e2d1fe9f5b36ec4ac45a2efd92a40d1 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Thu, 6 Jul 2023 17:12:09 +0200 Subject: [PATCH 029/402] ZFS: Fix invalid memcmp We ended up comparing over unset values as we had dnode_phys on one side and dnode on another Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/fs/zfs/zfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index d6a321f08..bd49ddb9e 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -2684,8 +2684,8 @@ dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type, blkid = objnum >> epbs; idx = objnum & ((1 << epbs) - 1); - if (data->dnode_buf != NULL && grub_memcmp (data->dnode_mdn, mdn, - sizeof (*mdn)) == 0 + if (data->dnode_buf != NULL && grub_memcmp (data->dnode_mdn, &mdn->dn, + sizeof (mdn->dn)) == 0 && objnum >= data->dnode_start && objnum < data->dnode_end) { grub_memmove (&(buf->dn), &(data->dnode_buf)[idx], DNODE_SIZE); From 444089eec6042250ce3a7184cb09bd8a2ab16808 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Thu, 6 Jul 2023 17:13:50 +0200 Subject: [PATCH 030/402] ZFS: Don't iterate over null objsets Reading them is harmless but useless as they are empty by definition Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/fs/zfs/zfs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index bd49ddb9e..221c914cb 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -4157,6 +4157,9 @@ iterate_zap_fs (const char *name, grub_uint64_t val, grub_err_t err; struct grub_dirhook_info info; + if (name[0] == 0 && val == 0) + return 0; + dnode_end_t mdn; err = dnode_get (&(ctx->data->mos), val, 0, &mdn, ctx->data); if (err) From 7ce5b4911005b2a0bfd716d92466b6711844068c Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Thu, 6 Jul 2023 17:16:00 +0200 Subject: [PATCH 031/402] ZFS: Check bonustype in addition to dnode type Some dnodes are shared with properties zap. This is used e.g. for quotas. Then dnode type is 0xc4 and GRUB stumbles on this. Check bonus type and if it's ok then ignore dnode type mismatch Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/fs/zfs/zfs.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index 221c914cb..b5453e006 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -3382,8 +3382,11 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol, if (!err) err = zap_lookup (&subvol->mdn, snapname, &headobj, data, 0); if (!err) - err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET, + err = dnode_get (&(data->mos), headobj, 0, &subvol->mdn, data); + if (!err && subvol->mdn.dn.dn_type != DMU_OT_DSL_DATASET && subvol->mdn.dn.dn_bonustype != DMU_OT_DSL_DATASET) + return grub_error(GRUB_ERR_BAD_FS, "incorrect dataset dnode type"); + if (err) { grub_free (fsname); @@ -4015,7 +4018,7 @@ fill_fs_info (struct grub_dirhook_info *info, info->dir = 1; - if (mdn.dn.dn_type == DMU_OT_DSL_DIR) + if (mdn.dn.dn_type == DMU_OT_DSL_DIR || mdn.dn.dn_bonustype == DMU_OT_DSL_DIR) { headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&mdn.dn))->dd_head_dataset_obj, mdn.endian); @@ -4167,8 +4170,10 @@ iterate_zap_fs (const char *name, grub_uint64_t val, grub_errno = 0; return 0; } - if (mdn.dn.dn_type != DMU_OT_DSL_DIR) + if (mdn.dn.dn_type != DMU_OT_DSL_DIR && mdn.dn.dn_bonustype != DMU_OT_DSL_DIR) { + grub_dprintf ("zfs", "type = 0x%x, val = 0x%llx\n", mdn.dn.dn_type, (long long)val); return 0; + } err = fill_fs_info (&info, &mdn, ctx->data); if (err) @@ -4198,7 +4203,7 @@ iterate_zap_snap (const char *name, grub_uint64_t val, return 0; } - if (mdn.dn.dn_type != DMU_OT_DSL_DATASET) + if (mdn.dn.dn_type != DMU_OT_DSL_DATASET && mdn.dn.dn_bonustype != DMU_OT_DSL_DATASET) return 0; err = fill_fs_info (&info, &mdn, ctx->data); @@ -4270,7 +4275,10 @@ grub_zfs_dir (grub_device_t device, const char *path, zap_iterate_u64 (&dn, iterate_zap_fs, data, &ctx); - err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET, &dn, data); + err = dnode_get (&(data->mos), headobj, 0, &dn, data); + if (!err && dn.dn.dn_type != DMU_OT_DSL_DATASET && dn.dn.dn_bonustype != DMU_OT_DSL_DATASET) + return grub_error(GRUB_ERR_BAD_FS, "incorrect dataset dnode type"); + if (err) { zfs_unmount (data); From 67ae3981dc5113e5af3a0539174bcd7eab8f7722 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 3 Aug 2023 15:24:09 +0200 Subject: [PATCH 032/402] loader/i386/linux: Prefer entry in long mode when booting via EFI The x86_64 Linux kernel can be booted in 32-bit mode, in which case the startup code creates a set of preliminary page tables that map the first 4 GiB of physical memory 1:1 and enables paging. This is a prerequisite for 64-bit execution and can therefore only be implemented in 32-bit code. The x86_64 Linux kernel can also be booted in 64-bit mode directly: this implies that paging is already enabled and it is the responsibility of the bootloader to ensure that the active page tables cover the entire loaded image, including its BSS space, the size of which is described in the image's setup header. Given that the EFI spec mandates execution in long mode for x86_64 and stipulates that all system memory is mapped 1:1, the Linux/x86 requirements for 64-bit entry can be met trivially when booting on x86_64 via EFI. So, enter via the 64-bit entry point in this case. This involves inspecting the xloadflags field in the setup header to check whether the 64-bit entry point is supported. This field was introduced in Linux version v3.8 (early 2013). This change ensures that all EFI firmware tables and other assets passed by the firmware or bootloader in memory remain mapped and accessible throughout the early startup code. Avoiding the drop out of long mode will also be needed to support upcoming CPU designs that no longer implement 32-bit mode at all (as recently announced by Intel [0]). [0] https://www.intel.com/content/www/us/en/developer/articles/technical/envisioning-future-simplified-architecture.html Cc: Daniel Kiper Cc: Julian Andres Klode Signed-off-by: Ard Biesheuvel Reviewed-by: Daniel Kiper --- grub-core/loader/i386/linux.c | 12 ++++++++++++ include/grub/i386/linux.h | 15 +++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index 997647a33..977757f2c 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -624,6 +624,18 @@ grub_linux_boot (void) } #endif +#if defined (__x86_64__) && defined (GRUB_MACHINE_EFI) + if (grub_le_to_cpu16 (ctx.params->version) >= 0x020c && + (linux_params.xloadflags & LINUX_X86_XLF_KERNEL_64) != 0) + { + struct grub_relocator64_efi_state state64; + + state64.rsi = ctx.real_mode_target; + state64.rip = ctx.params->code32_start + LINUX_X86_STARTUP64_OFFSET; + return grub_relocator64_efi_boot (relocator, state64); + } +#endif + /* FIXME. */ /* asm volatile ("lidt %0" : : "m" (idt_desc)); */ state.ebp = state.edi = state.ebx = 0; diff --git a/include/grub/i386/linux.h b/include/grub/i386/linux.h index 0fd6e1212..d4b550869 100644 --- a/include/grub/i386/linux.h +++ b/include/grub/i386/linux.h @@ -69,6 +69,16 @@ #define GRUB_LINUX_OFW_SIGNATURE \ (' ' << 24 | 'W' << 16 | 'F' << 8 | 'O') +#define LINUX_X86_XLF_KERNEL_64 (1<<0) +#define LINUX_X86_XLF_CAN_BE_LOADED_ABOVE_4G (1<<1) +#define LINUX_X86_XLF_EFI_HANDOVER_32 (1<<2) +#define LINUX_X86_XLF_EFI_HANDOVER_64 (1<<3) +#define LINUX_X86_XLF_EFI_KEXEC (1<<4) +#define LINUX_X86_XLF_5LEVEL (1<<5) +#define LINUX_X86_XLF_5LEVEL_ENABLED (1<<6) + +#define LINUX_X86_STARTUP64_OFFSET 0x200 + #ifndef ASM_FILE #define GRUB_E820_RAM 1 @@ -138,7 +148,7 @@ struct linux_i386_kernel_header grub_uint32_t kernel_alignment; grub_uint8_t relocatable; grub_uint8_t min_alignment; - grub_uint8_t pad[2]; + grub_uint16_t xloadflags; grub_uint32_t cmdline_size; grub_uint32_t hardware_subarch; grub_uint64_t hardware_subarch_data; @@ -315,7 +325,8 @@ struct linux_kernel_params grub_uint32_t initrd_addr_max; /* Maximum initrd address */ grub_uint32_t kernel_alignment; /* Alignment of the kernel */ grub_uint8_t relocatable_kernel; /* Is the kernel relocatable */ - grub_uint8_t pad1[3]; + grub_uint8_t min_alignment; + grub_uint16_t xloadflags; grub_uint32_t cmdline_size; /* Size of the kernel command line */ grub_uint32_t hardware_subarch; grub_uint64_t hardware_subarch_data; From 1f5b180742ff2706bc3a696d115ddbc677ec75b9 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 7 Aug 2023 14:21:51 +0200 Subject: [PATCH 033/402] loader/efi/linux: Implement x86 mixed mode using legacy boot Recent mixed-mode Linux kernels, i.e., v4.0 or newer, can access EFI runtime services at OS runtime even when the OS was not entered via the EFI stub. This is because, instead of reverting back to the firmware's segment selectors, GDTs and IDTs, the 64-bit kernel simply calls 32-bit runtime services using compatibility mode, i.e., the same mode used for 32-bit user space, without taking down all interrupt handling, exception handling, etc. This means that GRUB's legacy x86 boot mode is sufficient to make use of this: 32-bit i686 builds of GRUB can already boot 64-bit kernels in EFI enlightened mode, but without going via the EFI stub, and provide all the metadata that the OS needs to map the EFI runtime regions and call EFI runtime services successfully. It does mean that GRUB should not attempt to invoke the firmware's LoadImage()/StartImage() methods on kernel builds that it knows cannot be started natively. So, add a check for this in the native EFI boot path and fall back to legacy x86 mode in such cases. Note that in the general case, booting non-native images of the same native word size, e.g., x64 EFI apps on arm64 firmware, might be supported by means of emulation. So, let's only disallow images that use a non-native word size. This will also permit booting i686 kernels on x86_64 builds, although without access to runtime services, as this is not supported by Linux. This change on top of 2.12-rc1 is sufficient to boot ordinary Linux mixed mode builds and get full access to the EFI runtime services. Cc: Daniel Kiper Cc: Steve McIntyre Cc: Julian Andres Klode Signed-off-by: Ard Biesheuvel Acked-by: Dimitri John Ledkov Reviewed-by: Daniel Kiper --- grub-core/loader/efi/linux.c | 3 +++ include/grub/efi/pe32.h | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c index ab8fb35ad..bfbd95aee 100644 --- a/grub-core/loader/efi/linux.c +++ b/grub-core/loader/efi/linux.c @@ -117,6 +117,9 @@ grub_arch_efi_linux_load_image_header (grub_file_t file, return grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read COFF image header"); } + if (lh->pe_image_header.optional_header.magic != GRUB_PE32_NATIVE_MAGIC) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "non-native image not supported"); + /* * Linux kernels built for any architecture are guaranteed to support the * LoadFile2 based initrd loading protocol if the image version is >= 1. diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h index 101859af1..4e6e9d254 100644 --- a/include/grub/efi/pe32.h +++ b/include/grub/efi/pe32.h @@ -267,6 +267,12 @@ struct grub_pe32_section_table #define GRUB_PE32_SIGNATURE_SIZE 4 +#if GRUB_TARGET_SIZEOF_VOID_P == 8 +#define GRUB_PE32_NATIVE_MAGIC GRUB_PE32_PE64_MAGIC +#else +#define GRUB_PE32_NATIVE_MAGIC GRUB_PE32_PE32_MAGIC +#endif + struct grub_pe_image_header { /* This is always PE\0\0. */ From 084a66e307b9c0e54800afe1f2fc714fb475797b Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Fri, 11 Aug 2023 17:22:25 -0500 Subject: [PATCH 034/402] configure: Enable -fno-omit-frame-pointer for backtrace module The backtrace module is written assuming that the frame pointer is in %ebp. By default, -Os optimization level is used, which enables the gcc option -fomit-frame-pointer. This breaks the backtrace functionality. Enabling this may cause an unnoticeable performance cost and virtually no size increase. The backtrace command on x86_64 and probably i386 is broken due to the above rationale. I've not verified, but presumably the backtrace that used to be printed for an unhandled CPU exception is also broken. Do any distros handle this? Considering that, to my knowledge, no one has complained about this in the over 13 years that -Os has been used, has this code actually been useful? Is it worth disabling -fomit-frame-pointer? Though, I don't see much downside right now in disabling it. Alternatively, we could disable/remove the backtrace code. I think it would be nice to keep it and have it working. Nowadays, presumably QEMU makes the GDB stub rarely used as I imagine most are developing in a virtual machines. Also, the GDB stub does not work in UEFI. So, if anyone is using it on real hardware, they are doing so on pretty old machines. The lack of a GDB stub does not seem to be a pain point because no one has got it working on UEFI. This patch gets the backtrace command working on x86_64-efi in QEMU for me. However, it hangs when run on my laptop. Not sure what's going on there. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- configure.ac | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/configure.ac b/configure.ac index 27c5918fa..77e20ad02 100644 --- a/configure.ac +++ b/configure.ac @@ -1020,6 +1020,19 @@ if test x"$target_cpu" = xsparc64 ; then TARGET_LDFLAGS="$TARGET_LDFLAGS $grub_cv_target_cc_mno_relax" fi +# The backtrace module relies on frame pointers and the default optimization +# level, -Os, omits them. Make sure they are enabled. +AC_CACHE_CHECK([whether -fno-omit-frame-pointer works], [grub_cv_cc_fno_omit_frame_pointer], [ + CFLAGS="$TARGET_CFLAGS -fno-omit-frame-pointer" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_cc_fno_omit_frame_pointer=yes], + [grub_cv_cc_fno_omit_frame_pointer=no]) +]) + +if test "x$grub_cv_cc_fno_omit_frame_pointer" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -fno-omit-frame-pointer" +fi + # By default, GCC 4.4 generates .eh_frame sections containing unwind # information in some cases where it previously did not. GRUB doesn't need # these and they just use up vital space. Restore the old compiler From 3335591c6420ae373a24a4dc8f71d661b1fa3090 Mon Sep 17 00:00:00 2001 From: Xiaotian Wu Date: Thu, 24 Aug 2023 21:04:01 +0800 Subject: [PATCH 035/402] loongarch: Eliminate cmodel compilation warnings In the configure phase, the "-mcmodel=large" CFLAGS passed the test, but because it has not been implemented in gcc, the following warning will appear when compiling: gcc: warning: 'large' is not supported, now cmodel is set to 'normal' Signed-off-by: Xiaotian Wu Reviewed-by: Daniel Kiper --- configure.ac | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 77e20ad02..da9c8d560 100644 --- a/configure.ac +++ b/configure.ac @@ -1275,8 +1275,7 @@ AC_SUBST(TARGET_LDFLAGS_OLDMAGIC) LDFLAGS="$TARGET_LDFLAGS" -if test "$target_cpu" = x86_64 || test "$target_cpu" = sparc64 || test "$target_cpu" = riscv64 \ - || test "$target_cpu" = loongarch64 ; then +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" From db1faedccdce3cf83336155a95c04a8db03744c5 Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Mon, 11 Sep 2023 14:43:45 +0100 Subject: [PATCH 036/402] templates/linux_xen: Fix XSM entries generation It turns out that setting $xen_version in linux_entry_xsm() override $xen_version in the loop over $reverse_sorted_xen_list. This means that only one entry per Xen version is going to enable XSM, but all further entries are going to have "(XSM enabled)" in their titles without enabling XSM. When a "xenpolicy-$xen_version" file was found for the current $xen_version, it would overwrite $xen_version to add "(XSM enabled)" to the menu entry title. Once updated, the next call to linux_entry_xsm() would also have this modified $xen_version and would look for the file "xenpolicy-*(XSM enabled)" and fail. Signed-off-by: Anthony PERARD Reviewed-by: Daniel Kiper --- util/grub.d/20_linux_xen.in | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/util/grub.d/20_linux_xen.in b/util/grub.d/20_linux_xen.in index e46b757da..94dd8be13 100644 --- a/util/grub.d/20_linux_xen.in +++ b/util/grub.d/20_linux_xen.in @@ -98,7 +98,7 @@ linux_entry_xsm () { os="$1" version="$2" - xen_version="$3" + entry_xen_version="$3" type="$4" args="$5" xen_args="$6" @@ -107,25 +107,25 @@ linux_entry_xsm () # corresponding policy file. xenpolicy= if ${xsm} ; then - xenpolicy="xenpolicy-$xen_version" + xenpolicy="xenpolicy-$entry_xen_version" if test ! -e "${xen_dirname}/${xenpolicy}" ; then return fi xen_args="$xen_args flask=enforcing" - xen_version="$(gettext_printf "%s (XSM enabled)" "$xen_version")" - # xen_version is used for messages only; actual file is xen_basename + entry_xen_version="$(gettext_printf "%s (XSM enabled)" "$entry_xen_version")" + # entry_xen_version is used for messages only; actual file is xen_basename fi if [ -z "$boot_device_id" ]; then boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")" fi if [ x$type != xsimple ] ; then if [ x$type = xrecovery ] ; then - title="$(gettext_printf "%s, with Xen %s and Linux %s (recovery mode)" "${os}" "${xen_version}" "${version}")" + title="$(gettext_printf "%s, with Xen %s and Linux %s (recovery mode)" "${os}" "${entry_xen_version}" "${version}")" else - title="$(gettext_printf "%s, with Xen %s and Linux %s" "${os}" "${xen_version}" "${version}")" + title="$(gettext_printf "%s, with Xen %s and Linux %s" "${os}" "${entry_xen_version}" "${version}")" fi replacement_title="$(echo "Advanced options for ${OS}" | sed 's,>,>>,g')>$(echo "$title" | sed 's,>,>>,g')" - if [ x"Xen ${xen_version}>$title" = x"$GRUB_ACTUAL_DEFAULT" ]; then + if [ x"Xen ${entry_xen_version}>$title" = x"$GRUB_ACTUAL_DEFAULT" ]; then quoted="$(echo "$GRUB_ACTUAL_DEFAULT" | grub_quote)" title_correction_code="${title_correction_code}if [ \"x\$default\" = '$quoted' ]; then default='$(echo "$replacement_title" | grub_quote)'; fi;" grub_warn "$(gettext_printf "Please don't use old title \`%s' for GRUB_DEFAULT, use \`%s' (for versions before 2.00) or \`%s' (for 2.00 or later)" "$GRUB_ACTUAL_DEFAULT" "$replacement_title" "gnulinux-advanced-$boot_device_id>gnulinux-$version-$type-$boot_device_id")" @@ -143,7 +143,7 @@ linux_entry_xsm () prepare_boot_cache="$(prepare_grub_to_access_device ${GRUB_DEVICE_BOOT} | grub_add_tab)" fi printf '%s\n' "${prepare_boot_cache}" | sed "s/^/$submenu_indentation/" - xmessage="$(gettext_printf "Loading Xen %s ..." ${xen_version})" + xmessage="$(gettext_printf "Loading Xen %s ..." ${entry_xen_version})" lmessage="$(gettext_printf "Loading Linux %s ..." ${version})" sed "s/^/$submenu_indentation/" << EOF echo '$(echo "$xmessage" | grub_quote)' From 968928240a632483aea8550d3fad535eb7176625 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 8 Jul 2023 15:50:23 +0200 Subject: [PATCH 037/402] lib/i386/relocator64: Fix 64-bit FreeBSD boot on BIOS The commit 80948f532d (lib/i386/relocator64: Build fixes for i386) has broken 64-bit FreeBSD boot on BIOS. This patch fixes the issue. Fixes: 80948f532d (lib/i386/relocator64: Build fixes for i386) Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/lib/i386/relocator64.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/lib/i386/relocator64.S b/grub-core/lib/i386/relocator64.S index 00bf31599..c80538e7e 100644 --- a/grub-core/lib/i386/relocator64.S +++ b/grub-core/lib/i386/relocator64.S @@ -151,7 +151,7 @@ VARIABLE(grub_relocator64_rdx) #if defined (__APPLE__) || !defined (__x86_64__) .byte 0xff, 0x25 - .quad 0 + .long 0 #else jmp *LOCAL(jump_addr) (%rip) #endif From 1c2e1623068719941963dc34da6f927bafdbdbec Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Fri, 22 Sep 2023 14:34:17 -0500 Subject: [PATCH 038/402] util/grub-install-common: Minor improvements to printing of grub-mkimage command This is a preparatory patch to make the following patch less cluttered. The only visible change made here is to not print extra spaces when either or both --note or --disable-shim-lock are not given and to not print an extra space at the end of the command. The latter is done by constructing the trailing argument string with spaces in front of each argument rather than trailing. The allocation of the argument string is made precise, which has the benefit of saving a few bytes, but more importantly self-documenting what the needed allocated bytes are. Also, unneeded braces are removed from an if block. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- util/grub-install-common.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/util/grub-install-common.c b/util/grub-install-common.c index 52a29d1cb..f9b9201c8 100644 --- a/util/grub-install-common.c +++ b/util/grub-install-common.c @@ -617,60 +617,58 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix, int dc = decompressors (); if (memdisk_path) - slen += 20 + grub_strlen (memdisk_path); + slen += sizeof (" --memdisk ''") + grub_strlen (memdisk_path); if (config_path) - slen += 20 + grub_strlen (config_path); + slen += sizeof (" --config ''") + grub_strlen (config_path); for (pk = pubkeys; pk < pubkeys + npubkeys; pk++) - slen += 20 + grub_strlen (*pk); + slen += sizeof (" --pubkey ''") + grub_strlen (*pk); for (md = modules.entries; *md; md++) - { - slen += 10 + grub_strlen (*md); - } + slen += sizeof (" ''") + grub_strlen (*md); p = s = xmalloc (slen); if (memdisk_path) { + *p++ = ' '; p = grub_stpcpy (p, "--memdisk '"); p = grub_stpcpy (p, memdisk_path); *p++ = '\''; - *p++ = ' '; } if (config_path) { + *p++ = ' '; p = grub_stpcpy (p, "--config '"); p = grub_stpcpy (p, config_path); *p++ = '\''; - *p++ = ' '; } for (pk = pubkeys; pk < pubkeys + npubkeys; pk++) { + *p++ = ' '; p = grub_stpcpy (p, "--pubkey '"); p = grub_stpcpy (p, *pk); *p++ = '\''; - *p++ = ' '; } for (md = modules.entries; *md; md++) { + *p++ = ' '; *p++ = '\''; p = grub_stpcpy (p, *md); *p++ = '\''; - *p++ = ' '; } *p = '\0'; - grub_util_info ("grub-mkimage --directory '%s' --prefix '%s'" - " --output '%s' " + grub_util_info ("grub-mkimage --directory '%s' --prefix '%s' --output '%s'" " --dtb '%s' " "--sbat '%s' " - "--format '%s' --compression '%s' %s %s %s\n", - dir, prefix, - outname, dtb ? : "", sbat ? : "", mkimage_target, - compnames[compression], note ? "--note" : "", - disable_shim_lock ? "--disable-shim-lock" : "", s); + "--format '%s' --compression '%s'%s%s%s\n", + dir, prefix, outname, + dtb ? : "", sbat ? : "", + mkimage_target, compnames[compression], + note ? " --note" : "", + disable_shim_lock ? " --disable-shim-lock" : "", s); free (s); tgt = grub_install_get_image_target (mkimage_target); From 28a509dd58cee75e43f19e38ff4c5787048aecb9 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Fri, 22 Sep 2023 14:34:18 -0500 Subject: [PATCH 039/402] util/grub-install-common: Print usable grub-mkimage command When grub-install is run with the verbose option, it will print a log message indicating the grub-mkimage command and arguments used. GRUB no longer calls the grub-mkimage binary internally, however the command logged is a command that if run should effectively be what grub-install used. However, as this has changed some of the newer options have been incorrectly added so that the printed command fails when run separately. This change makes the displayed command run as intended. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- util/grub-install-common.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/util/grub-install-common.c b/util/grub-install-common.c index f9b9201c8..ce854d86f 100644 --- a/util/grub-install-common.c +++ b/util/grub-install-common.c @@ -620,6 +620,10 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix, slen += sizeof (" --memdisk ''") + grub_strlen (memdisk_path); if (config_path) slen += sizeof (" --config ''") + grub_strlen (config_path); + if (dtb) + slen += sizeof (" --dtb ''") + grub_strlen (dtb); + if (sbat) + slen += sizeof (" --sbat ''") + grub_strlen (sbat); for (pk = pubkeys; pk < pubkeys + npubkeys; pk++) slen += sizeof (" --pubkey ''") + grub_strlen (*pk); @@ -642,6 +646,20 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix, p = grub_stpcpy (p, config_path); *p++ = '\''; } + if (dtb) + { + *p++ = ' '; + p = grub_stpcpy (p, "--dtb '"); + p = grub_stpcpy (p, dtb); + *p++ = '\''; + } + if (sbat) + { + *p++ = ' '; + p = grub_stpcpy (p, "--sbat '"); + p = grub_stpcpy (p, sbat); + *p++ = '\''; + } for (pk = pubkeys; pk < pubkeys + npubkeys; pk++) { *p++ = ' '; @@ -661,11 +679,8 @@ 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'" - " --dtb '%s' " - "--sbat '%s' " - "--format '%s' --compression '%s'%s%s%s\n", + " --format '%s' --compression '%s'%s%s%s\n", dir, prefix, outname, - dtb ? : "", sbat ? : "", mkimage_target, compnames[compression], note ? " --note" : "", disable_shim_lock ? " --disable-shim-lock" : "", s); From 48f569c78a496d3e11a4605b0999bc34fa5bc977 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Mon, 25 Sep 2023 13:58:18 +0800 Subject: [PATCH 040/402] kern/acpi: Skip NULL entries in RSDT and XSDT During attempts to configure a serial console, a Page Fault Exception and system reset were encountered, specifically on release 2.12~rc1. This issue was not present in prior versions and seemed to affect only a specific machine, potentially pointing to hardware or firmware flaw. After investigation, it was discovered that the invalid page access occurred during the discovery of serial MMIO ports as specified by ACPI's SPCR table [1]. The recent change uncovered an issue in GRUB's ACPI driver. In certain cases, the XSDT/RSDT root table might contain a NULL entry as a terminator, depending on how the tables are assembled. GRUB cannot blindly trust the address in the root table to be valid and should perform a sanity check for NULL entries. This patch introduces this simple check. This fix is also inspired by a related Linux kernel fix [2]. [1] 7b192ec4c term/ns8250: Use ACPI SPCR table when available to configure serial [2] 0f929fbf0 ACPICA: Tables: Add new mechanism to skip NULL entries in RSDT and XSDT. Signed-off-by: Michael Chang Reviewed-by: Daniel Kiper --- grub-core/kern/acpi.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/grub-core/kern/acpi.c b/grub-core/kern/acpi.c index c61115dcd..48ded4e2e 100644 --- a/grub-core/kern/acpi.c +++ b/grub-core/kern/acpi.c @@ -51,6 +51,10 @@ grub_acpi_rsdt_find_table (struct grub_acpi_table_header *rsdt, const char *sig) for (; s; s--, ptr++) { struct grub_acpi_table_header *tbl; + + /* Skip NULL entries in RSDT/XSDT. */ + if (!ptr->val) + continue; tbl = (struct grub_acpi_table_header *) (grub_addr_t) ptr->val; if (grub_memcmp (tbl->signature, sig, 4) == 0) return tbl; @@ -75,6 +79,10 @@ grub_acpi_xsdt_find_table (struct grub_acpi_table_header *xsdt, const char *sig) for (; s; s--, ptr++) { struct grub_acpi_table_header *tbl; + + /* Skip NULL entries in RSDT/XSDT. */ + if (!ptr->val) + continue; #if GRUB_CPU_SIZEOF_VOID_P != 8 if (ptr->val >> 32) continue; From 43651027d24e62a7a463254165e1e46e42aecdea Mon Sep 17 00:00:00 2001 From: Maxim Suhanov Date: Mon, 28 Aug 2023 16:31:57 +0300 Subject: [PATCH 041/402] fs/ntfs: Fix an OOB write when parsing the $ATTRIBUTE_LIST attribute for the $MFT file When parsing an extremely fragmented $MFT file, i.e., the file described using the $ATTRIBUTE_LIST attribute, current NTFS code will reuse a buffer containing bytes read from the underlying drive to store sector numbers, which are consumed later to read data from these sectors into another buffer. These sectors numbers, two 32-bit integers, are always stored at predefined offsets, 0x10 and 0x14, relative to first byte of the selected entry within the $ATTRIBUTE_LIST attribute. Usually, this won't cause any problem. However, when parsing a specially-crafted file system image, this may cause the NTFS code to write these integers beyond the buffer boundary, likely causing the GRUB memory allocator to misbehave or fail. These integers contain values which are controlled by on-disk structures of the NTFS file system. Such modification and resulting misbehavior may touch a memory range not assigned to the GRUB and owned by firmware or another EFI application/driver. This fix introduces checks to ensure that these sector numbers are never written beyond the boundary. Fixes: CVE-2023-4692 Reported-by: Maxim Suhanov Signed-off-by: Maxim Suhanov Reviewed-by: Daniel Kiper --- grub-core/fs/ntfs.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index bbdbe24ad..c3c4db117 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -184,7 +184,7 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) } if (at->attr_end) { - grub_uint8_t *pa; + grub_uint8_t *pa, *pa_end; at->emft_buf = grub_malloc (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR); if (at->emft_buf == NULL) @@ -209,11 +209,13 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) } at->attr_nxt = at->edat_buf; at->attr_end = at->edat_buf + u32at (pa, 0x30); + pa_end = at->edat_buf + n; } else { at->attr_nxt = at->attr_end + u16at (pa, 0x14); at->attr_end = at->attr_end + u32at (pa, 4); + pa_end = at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR); } at->flags |= GRUB_NTFS_AF_ALST; while (at->attr_nxt < at->attr_end) @@ -230,6 +232,13 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) at->flags |= GRUB_NTFS_AF_GPOS; at->attr_cur = at->attr_nxt; pa = at->attr_cur; + + if ((pa >= pa_end) || (pa_end - pa < 0x18)) + { + grub_error (GRUB_ERR_BAD_FS, "can\'t parse attribute list"); + return NULL; + } + grub_set_unaligned32 ((char *) pa + 0x10, grub_cpu_to_le32 (at->mft->data->mft_start)); grub_set_unaligned32 ((char *) pa + 0x14, @@ -240,6 +249,13 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) { if (*pa != attr) break; + + if ((pa >= pa_end) || (pa_end - pa < 0x18)) + { + grub_error (GRUB_ERR_BAD_FS, "can\'t parse attribute list"); + return NULL; + } + if (read_attr (at, pa + 0x10, u32at (pa, 0x10) * (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR), From 0ed2458cc4eff6d9a9199527e2a0b6d445802f94 Mon Sep 17 00:00:00 2001 From: Maxim Suhanov Date: Mon, 28 Aug 2023 16:32:33 +0300 Subject: [PATCH 042/402] fs/ntfs: Fix an OOB read when reading data from the resident $DATA attribute When reading a file containing resident data, i.e., the file data is stored in the $DATA attribute within the NTFS file record, not in external clusters, there are no checks that this resident data actually fits the corresponding file record segment. When parsing a specially-crafted file system image, the current NTFS code will read the file data from an arbitrary, attacker-chosen memory offset and of arbitrary, attacker-chosen length. This allows an attacker to display arbitrary chunks of memory, which could contain sensitive information like password hashes or even plain-text, obfuscated passwords from BS EFI variables. This fix implements a check to ensure that resident data is read from the corresponding file record segment only. Fixes: CVE-2023-4693 Reported-by: Maxim Suhanov Signed-off-by: Maxim Suhanov Reviewed-by: Daniel Kiper --- grub-core/fs/ntfs.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index c3c4db117..a68e173d8 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -401,7 +401,18 @@ read_data (struct grub_ntfs_attr *at, grub_uint8_t *pa, grub_uint8_t *dest, { if (ofs + len > u32at (pa, 0x10)) return grub_error (GRUB_ERR_BAD_FS, "read out of range"); - grub_memcpy (dest, pa + u32at (pa, 0x14) + ofs, len); + + if (u32at (pa, 0x10) > (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) + return grub_error (GRUB_ERR_BAD_FS, "resident attribute too large"); + + if (pa >= at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) + return grub_error (GRUB_ERR_BAD_FS, "resident attribute out of range"); + + if (u16at (pa, 0x14) + u32at (pa, 0x10) > + (grub_addr_t) at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR) - (grub_addr_t) pa) + return grub_error (GRUB_ERR_BAD_FS, "resident attribute out of range"); + + grub_memcpy (dest, pa + u16at (pa, 0x14) + ofs, len); return 0; } From 7e5f031a6a6a3decc2360a7b0c71abbe598e7354 Mon Sep 17 00:00:00 2001 From: Maxim Suhanov Date: Mon, 28 Aug 2023 16:33:17 +0300 Subject: [PATCH 043/402] fs/ntfs: Fix an OOB read when parsing directory entries from resident and non-resident index attributes This fix introduces checks to ensure that index entries are never read beyond the corresponding directory index. The lack of this check is a minor issue, likely not exploitable in any way. Reported-by: Maxim Suhanov Signed-off-by: Maxim Suhanov Reviewed-by: Daniel Kiper --- grub-core/fs/ntfs.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index a68e173d8..2d78b96e1 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -599,7 +599,7 @@ get_utf8 (grub_uint8_t *in, grub_size_t len) } static int -list_file (struct grub_ntfs_file *diro, grub_uint8_t *pos, +list_file (struct grub_ntfs_file *diro, grub_uint8_t *pos, grub_uint8_t *end_pos, grub_fshelp_iterate_dir_hook_t hook, void *hook_data) { grub_uint8_t *np; @@ -610,6 +610,9 @@ list_file (struct grub_ntfs_file *diro, grub_uint8_t *pos, grub_uint8_t namespace; char *ustr; + if ((pos >= end_pos) || (end_pos - pos < 0x52)) + break; + if (pos[0xC] & 2) /* end signature */ break; @@ -617,6 +620,9 @@ list_file (struct grub_ntfs_file *diro, grub_uint8_t *pos, ns = *(np++); namespace = *(np++); + if (2 * ns > end_pos - pos - 0x52) + break; + /* * Ignore files in DOS namespace, as they will reappear as Win32 * names. @@ -806,7 +812,9 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, } cur_pos += 0x10; /* Skip index root */ - ret = list_file (mft, cur_pos + u16at (cur_pos, 0), hook, hook_data); + ret = list_file (mft, cur_pos + u16at (cur_pos, 0), + at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR), + hook, hook_data); if (ret) goto done; @@ -893,6 +901,7 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, (const grub_uint8_t *) "INDX"))) goto done; ret = list_file (mft, &indx[0x18 + u16at (indx, 0x18)], + indx + (mft->data->idx_size << GRUB_NTFS_BLK_SHR), hook, hook_data); if (ret) goto done; From 7a5a116739fa6d8a625da7d6b9272c9a2462f967 Mon Sep 17 00:00:00 2001 From: Maxim Suhanov Date: Mon, 28 Aug 2023 16:33:44 +0300 Subject: [PATCH 044/402] fs/ntfs: Fix an OOB read when parsing bitmaps for index attributes This fix introduces checks to ensure that bitmaps for directory indices are never read beyond their actual sizes. The lack of this check is a minor issue, likely not exploitable in any way. Reported-by: Maxim Suhanov Signed-off-by: Maxim Suhanov Reviewed-by: Daniel Kiper --- grub-core/fs/ntfs.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index 2d78b96e1..bb70c89fb 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -843,6 +843,25 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, if (is_resident) { + if (bitmap_len > (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) + { + grub_error (GRUB_ERR_BAD_FS, "resident bitmap too large"); + goto done; + } + + if (cur_pos >= at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) + { + grub_error (GRUB_ERR_BAD_FS, "resident bitmap out of range"); + goto done; + } + + if (u16at (cur_pos, 0x14) + u32at (cur_pos, 0x10) > + (grub_addr_t) at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR) - (grub_addr_t) cur_pos) + { + grub_error (GRUB_ERR_BAD_FS, "resident bitmap out of range"); + goto done; + } + grub_memcpy (bmp, cur_pos + u16at (cur_pos, 0x14), bitmap_len); } From 1fe82c41e070385e273d7bb1cfb482627a3c28e8 Mon Sep 17 00:00:00 2001 From: Maxim Suhanov Date: Mon, 28 Aug 2023 16:38:19 +0300 Subject: [PATCH 045/402] fs/ntfs: Fix an OOB read when parsing a volume label This fix introduces checks to ensure that an NTFS volume label is always read from the corresponding file record segment. The current NTFS code allows the volume label string to be read from an arbitrary, attacker-chosen memory location. However, the bytes read are always treated as UTF-16LE. So, the final string displayed is mostly unreadable and it can't be easily converted back to raw bytes. The lack of this check is a minor issue, likely not causing a significant data leak. Reported-by: Maxim Suhanov Signed-off-by: Maxim Suhanov Reviewed-by: Daniel Kiper --- grub-core/fs/ntfs.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index bb70c89fb..ff5e3740f 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -1213,13 +1213,29 @@ grub_ntfs_label (grub_device_t device, char **label) init_attr (&mft->attr, mft); pa = find_attr (&mft->attr, GRUB_NTFS_AT_VOLUME_NAME); + + if (pa >= mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR)) + { + grub_error (GRUB_ERR_BAD_FS, "can\'t parse volume label"); + goto fail; + } + + if (mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR) - pa < 0x16) + { + grub_error (GRUB_ERR_BAD_FS, "can\'t parse volume label"); + goto fail; + } + if ((pa) && (pa[8] == 0) && (u32at (pa, 0x10))) { int len; len = u32at (pa, 0x10) / 2; pa += u16at (pa, 0x14); - *label = get_utf8 (pa, len); + if (mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR) - pa >= 2 * len) + *label = get_utf8 (pa, len); + else + grub_error (GRUB_ERR_BAD_FS, "can\'t parse volume label"); } fail: From e58b870ff926415e23fc386af41ff81b2f588763 Mon Sep 17 00:00:00 2001 From: Maxim Suhanov Date: Mon, 28 Aug 2023 16:40:07 +0300 Subject: [PATCH 046/402] fs/ntfs: Make code more readable Move some calls used to access NTFS attribute header fields into functions with human-readable names. Suggested-by: Daniel Kiper Signed-off-by: Maxim Suhanov Reviewed-by: Daniel Kiper --- grub-core/fs/ntfs.c | 48 +++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index ff5e3740f..de435aa14 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -52,6 +52,24 @@ u64at (void *ptr, grub_size_t ofs) return grub_le_to_cpu64 (grub_get_unaligned64 ((char *) ptr + ofs)); } +static grub_uint16_t +first_attr_off (void *mft_buf_ptr) +{ + return u16at (mft_buf_ptr, 0x14); +} + +static grub_uint16_t +res_attr_data_off (void *res_attr_ptr) +{ + return u16at (res_attr_ptr, 0x14); +} + +static grub_uint32_t +res_attr_data_len (void *res_attr_ptr) +{ + return u32at (res_attr_ptr, 0x10); +} + grub_ntfscomp_func_t grub_ntfscomp_func; static grub_err_t @@ -106,7 +124,7 @@ init_attr (struct grub_ntfs_attr *at, struct grub_ntfs_file *mft) { at->mft = mft; at->flags = (mft == &mft->data->mmft) ? GRUB_NTFS_AF_MMFT : 0; - at->attr_nxt = mft->buf + u16at (mft->buf, 0x14); + at->attr_nxt = mft->buf + first_attr_off (mft->buf); at->attr_end = at->emft_buf = at->edat_buf = at->sbuf = NULL; } @@ -154,7 +172,7 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) return NULL; } - new_pos = &at->emft_buf[u16at (at->emft_buf, 0x14)]; + new_pos = &at->emft_buf[first_attr_off (at->emft_buf)]; while (*new_pos != 0xFF) { if ((*new_pos == *at->attr_cur) @@ -213,7 +231,7 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) } else { - at->attr_nxt = at->attr_end + u16at (pa, 0x14); + at->attr_nxt = at->attr_end + res_attr_data_off (pa); at->attr_end = at->attr_end + u32at (pa, 4); pa_end = at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR); } @@ -399,20 +417,20 @@ read_data (struct grub_ntfs_attr *at, grub_uint8_t *pa, grub_uint8_t *dest, if (pa[8] == 0) { - if (ofs + len > u32at (pa, 0x10)) + if (ofs + len > res_attr_data_len (pa)) return grub_error (GRUB_ERR_BAD_FS, "read out of range"); - if (u32at (pa, 0x10) > (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) + if (res_attr_data_len (pa) > (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) return grub_error (GRUB_ERR_BAD_FS, "resident attribute too large"); if (pa >= at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) return grub_error (GRUB_ERR_BAD_FS, "resident attribute out of range"); - if (u16at (pa, 0x14) + u32at (pa, 0x10) > + if (res_attr_data_off (pa) + res_attr_data_len (pa) > (grub_addr_t) at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR) - (grub_addr_t) pa) return grub_error (GRUB_ERR_BAD_FS, "resident attribute out of range"); - grub_memcpy (dest, pa + u16at (pa, 0x14) + ofs, len); + grub_memcpy (dest, pa + res_attr_data_off (pa) + ofs, len); return 0; } @@ -556,7 +574,7 @@ init_file (struct grub_ntfs_file *mft, grub_uint64_t mftno) (unsigned long long) mftno); if (!pa[8]) - mft->size = u32at (pa, 0x10); + mft->size = res_attr_data_len (pa); else mft->size = u64at (pa, 0x30); @@ -805,7 +823,7 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, (u32at (cur_pos, 0x18) != 0x490024) || (u32at (cur_pos, 0x1C) != 0x300033)) continue; - cur_pos += u16at (cur_pos, 0x14); + cur_pos += res_attr_data_off (cur_pos); if (*cur_pos != 0x30) /* Not filename index */ continue; break; @@ -834,7 +852,7 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, { int is_resident = (cur_pos[8] == 0); - bitmap_len = ((is_resident) ? u32at (cur_pos, 0x10) : + bitmap_len = ((is_resident) ? res_attr_data_len (cur_pos) : u32at (cur_pos, 0x28)); bmp = grub_malloc (bitmap_len); @@ -855,14 +873,14 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, goto done; } - if (u16at (cur_pos, 0x14) + u32at (cur_pos, 0x10) > + if (res_attr_data_off (cur_pos) + res_attr_data_len (cur_pos) > (grub_addr_t) at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR) - (grub_addr_t) cur_pos) { grub_error (GRUB_ERR_BAD_FS, "resident bitmap out of range"); goto done; } - grub_memcpy (bmp, cur_pos + u16at (cur_pos, 0x14), + grub_memcpy (bmp, cur_pos + res_attr_data_off (cur_pos), bitmap_len); } else @@ -1226,12 +1244,12 @@ grub_ntfs_label (grub_device_t device, char **label) goto fail; } - if ((pa) && (pa[8] == 0) && (u32at (pa, 0x10))) + if ((pa) && (pa[8] == 0) && (res_attr_data_len (pa))) { int len; - len = u32at (pa, 0x10) / 2; - pa += u16at (pa, 0x14); + len = res_attr_data_len (pa) / 2; + pa += res_attr_data_off (pa); if (mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR) - pa >= 2 * len) *label = get_utf8 (pa, len); else From a19e47ca412c9f8acfdb0eb663f6cb2aaf5cf8cb Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 14 Jul 2023 08:12:30 +0200 Subject: [PATCH 047/402] commands/efi/lsefisystab: Print the UEFI specification revision in human readable form E.g. 2.10 instead of 00020064 and 2.3.1 instead of 0002001f. See UEFI 2.10 specification, chapter 4.2.1 EFI_TABLE_HEADER. Signed-off-by: Heinrich Schuchardt Reviewed-by: Daniel Kiper --- grub-core/commands/efi/lsefisystab.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/grub-core/commands/efi/lsefisystab.c b/grub-core/commands/efi/lsefisystab.c index 79ccee12a..ffb24fc3b 100644 --- a/grub-core/commands/efi/lsefisystab.c +++ b/grub-core/commands/efi/lsefisystab.c @@ -64,12 +64,18 @@ grub_cmd_lsefisystab (struct grub_command *cmd __attribute__ ((unused)), char **args __attribute__ ((unused))) { const grub_efi_system_table_t *st = grub_efi_system_table; + const grub_efi_uint32_t major_rev = st->hdr.revision >> 16; + const grub_efi_uint32_t minor_rev_upper = (st->hdr.revision & 0xffff) / 10; + const grub_efi_uint32_t minor_rev_lower = (st->hdr.revision & 0xffff) % 10; grub_efi_configuration_table_t *t; unsigned int i; grub_printf ("Address: %p\n", st); - grub_printf ("Signature: %016" PRIxGRUB_UINT64_T " revision: %08x\n", - st->hdr.signature, st->hdr.revision); + grub_printf ("Signature: %016" PRIxGRUB_UINT64_T " revision: %u.%u", + st->hdr.signature, major_rev, minor_rev_upper); + if (minor_rev_lower) + grub_printf (".%u", minor_rev_lower); + grub_printf ("\n"); { char *vendor; grub_uint16_t *vendor_utf16; From f7a663c007dcaf97223112660bad51ef6c0ca09e Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Fri, 14 Jul 2023 15:33:19 -0500 Subject: [PATCH 048/402] term/serial: Ensure proper NULL termination after grub_strncpy() A large enough argument to the --port option could cause a string buffer to be not NULL terminated because grub_strncpy() does not guarantee NULL termination if copied string is longer than max characters to copy. Fixes: 712309eaae04 (term/serial: Use grub_strncpy() instead of grub_snprintf() when only copying string) Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- grub-core/term/serial.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/grub-core/term/serial.c b/grub-core/term/serial.c index 869555430..8260dcb7a 100644 --- a/grub-core/term/serial.c +++ b/grub-core/term/serial.c @@ -257,7 +257,10 @@ grub_cmd_serial (grub_extcmd_context_t ctxt, int argc, char **args) { if (grub_strncmp (state[OPTION_PORT].arg, "mmio,", sizeof ("mmio,") - 1) == 0 || grub_strncmp (state[OPTION_PORT].arg, "pci,", sizeof ("pci,") - 1) == 0) - grub_strncpy (pname, state[1].arg, sizeof (pname)); + { + grub_strncpy (pname, state[1].arg, sizeof (pname)); + pname[sizeof (pname) - 1] = '\0'; + } else grub_snprintf (pname, sizeof (pname), "port%lx", grub_strtoul (state[1].arg, 0, 0)); From 016f1425776afdb02601b193ada8f0b5cf983e46 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Fri, 14 Jul 2023 15:49:17 -0500 Subject: [PATCH 049/402] disk/cryptodisk: Optimize luks_script_get() Use the return value of grub_snprintf() to move the string pointer forward, instead of incrementing the string pointer iteratively until a NULL byte is reached. Move the space out of the format string argument, a small optimization, but also makes the spacing clearer. Also, use the new PRIxGRUB_OFFSET instead of PRIuGRUB_UINT64_T to accurately reflect the format string for this type. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- grub-core/disk/cryptodisk.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index fb807e2a6..d40bdc060 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -1504,9 +1504,8 @@ luks_script_get (grub_size_t *sz) ptr = grub_stpcpy (ptr, "luks_mount "); ptr = grub_stpcpy (ptr, i->uuid); *ptr++ = ' '; - grub_snprintf (ptr, 21, "%" PRIuGRUB_UINT64_T " ", i->offset_sectors); - while (*ptr) - ptr++; + ptr += grub_snprintf (ptr, 21, "%" PRIxGRUB_OFFSET, i->offset_sectors); + *ptr++ = ' '; for (iptr = i->cipher->cipher->name; *iptr; iptr++) *ptr++ = grub_tolower (*iptr); switch (i->mode) From 95963d97f8d87f209e2032283c79912e19b1de82 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Fri, 14 Jul 2023 15:49:18 -0500 Subject: [PATCH 050/402] disk/cryptodisk: Add support for LUKS2 in (proc)/luks_script The sector size in bytes is added to each line and it is allowed to be 6 decimal digits long, which covers the most common cases of 512 and 4096 byte sectors with space for two additional digits as future-proofing. The size allocation is updated to reflect this additional field. Also make clearer the size allocation calculation. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- grub-core/disk/cryptodisk.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index d40bdc060..2246af51b 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -1479,12 +1479,22 @@ luks_script_get (grub_size_t *sz) *sz = 0; for (i = cryptodisk_list; i != NULL; i = i->next) - if (grub_strcmp (i->modname, "luks") == 0) + if (grub_strcmp (i->modname, "luks") == 0 || + grub_strcmp (i->modname, "luks2") == 0) { - size += sizeof ("luks_mount "); + size += grub_strlen (i->modname); + size += sizeof ("_mount"); size += grub_strlen (i->uuid); size += grub_strlen (i->cipher->cipher->name); - size += 54; + /* + * Add space in the line for (in order) spaces, cipher mode, cipher IV + * mode, sector offset, sector size and the trailing newline. This is + * an upper bound on the size of this data. There are 15 extra bytes + * in an earlier version of this code that are unaccounted for. It is + * left in the calculations in case it is needed. At worst, its short- + * lived wasted space. + */ + size += 5 + 5 + 8 + 20 + 6 + 1 + 15; if (i->essiv_hash) size += grub_strlen (i->essiv_hash->name); size += i->keysize * 2; @@ -1497,15 +1507,19 @@ luks_script_get (grub_size_t *sz) ptr = ret; for (i = cryptodisk_list; i != NULL; i = i->next) - if (grub_strcmp (i->modname, "luks") == 0) + if (grub_strcmp (i->modname, "luks") == 0 || + grub_strcmp (i->modname, "luks2") == 0) { unsigned j; const char *iptr; - ptr = grub_stpcpy (ptr, "luks_mount "); + ptr = grub_stpcpy (ptr, i->modname); + ptr = grub_stpcpy (ptr, "_mount "); ptr = grub_stpcpy (ptr, i->uuid); *ptr++ = ' '; ptr += grub_snprintf (ptr, 21, "%" PRIxGRUB_OFFSET, i->offset_sectors); *ptr++ = ' '; + ptr += grub_snprintf (ptr, 7, "%u", 1 << i->log_sector_size); + *ptr++ = ' '; for (iptr = i->cipher->cipher->name; *iptr; iptr++) *ptr++ = grub_tolower (*iptr); switch (i->mode) From c3bdf263f63b57394fee836cde3e582a4576e6fd Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Tue, 18 Jul 2023 00:47:14 -0500 Subject: [PATCH 051/402] kern/efi/init: Disable stack smashing protection on grub_efi_init() GCC is electing to instrument grub_efi_init() to give it stack smashing protection when configuring with --enable-stack-protector on the x86_64-efi target. In the function prologue, the canary at the top of the stack frame is set to the value of the stack guard. And in the epilogue, the canary is checked to verify if it is equal to the guard and if not to call the stack check fail function. The issue is that grub_efi_init() sets up the guard by initializing it with random bytes, if the firmware supports the RNG protocol. So in its prologue the canary will be set with the value of the uninitialized guard, likely NUL bytes. Then the guard is initialized, and finally the epilogue checks the canary against the guard, which will almost certainly be different. This causes the code path for a smashed stack to be taken, causing the machine to print out a message that stack smashing was detected, wait 5 seconds, and then reboot. Disable grub_efi_init() instrumentation so there is no stack smashing false positive generated. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- grub-core/kern/efi/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c index 6fe1ff8c8..e759cc315 100644 --- a/grub-core/kern/efi/init.c +++ b/grub-core/kern/efi/init.c @@ -102,7 +102,7 @@ stack_protector_init (void) grub_addr_t grub_modbase; -void +__attribute__ ((__optimize__ ("-fno-stack-protector"))) void grub_efi_init (void) { grub_modbase = grub_efi_section_addr ("mods"); From cf58eca2a2f55a8086a7cbd37c5b6435c168548a Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Thu, 31 Aug 2023 23:50:56 -0500 Subject: [PATCH 052/402] tests/util/grub-shell: Enable RNG device to better test stack smashing In certain firmwares, e.g. OVMF, the RNG protocol is not enabled unless there is an RNG device. When not enabled, GRUB fails to initialize the stack guard with random bytes. For testing, this is not a big issue, but there have been bugs found in the initialization. So turn this on for EFI platforms to catch any regressions. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- tests/util/grub-shell.in | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/util/grub-shell.in b/tests/util/grub-shell.in index 715e1c2e6..496e1bab3 100644 --- a/tests/util/grub-shell.in +++ b/tests/util/grub-shell.in @@ -225,6 +225,13 @@ case "${grub_modinfo_target_cpu}-${grub_modinfo_platform}" in console=console;; esac +case "${grub_modinfo_target_cpu}-${grub_modinfo_platform}" in + # Only add the RNG device for EFI platforms because we currently only + # support Stack Smashing protection on EFI. + *-efi) + qemuopts="$qemuopts -device virtio-rng-pci" ;; +esac + timeout=${GRUB_SHELL_DEFAULT_TIMEOUT:-60} mkimage_extra_arg= debug=${GRUB_SHELL_DEFAULT_DEBUG:-$GRUB_TEST_DEFAULT_DEBUG} From 4bcf6f747c3ab0b998c6f5a361804e38bc9c4334 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Wed, 4 Oct 2023 11:32:35 -0400 Subject: [PATCH 053/402] kern/ieee1275/init: Restrict high memory in presence of fadump on ppc64 When a kernel dump is present then restrict the high memory regions to avoid allocating memory where the kernel dump resides. Use the ibm,kernel-dump node under /rtas to determine whether a kernel dump exists and up to which limit GRUB can use available memory. Set the upper_mem_limit to the size of the kernel dump section of type REAL_MODE_REGION and therefore only allow GRUB's memory usage for high addresses from RMO_ADDR_MAX to upper_mem_limit. This means that GRUB can use high memory in the range of RMO_ADDR_MAX (768MB) to upper_mem_limit and the kernel-dump memory regions above upper_mem_limit remain untouched. This change has no effect on memory allocations below linux_rmo_save (typically at 640MB). Also, fall back to allocating below rmo_linux_save in case the chunk of memory there would be larger than the chunk of memory above RMO_ADDR_MAX. This can for example occur if a free memory area is found starting at 300MB extending up to 1GB but a kernel dump is located at 768MB and therefore does not allow the allocation of the high memory area but requiring to use the chunk starting at 300MB to avoid an unnecessary out-of-memory condition. Signed-off-by: Stefan Berger Reviewed-by: Hari Bathini Cc: Pavithra Prakash Cc: Michael Ellerman Cc: Carolyn Scherrer Cc: Mahesh Salgaonkar Cc: Sourabh Jain Reviewed-by: Daniel Kiper --- grub-core/kern/ieee1275/init.c | 144 ++++++++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 2 deletions(-) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index bd9a4804b..d6c9c9049 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -17,6 +17,8 @@ * along with GRUB. If not, see . */ +#include /* offsetof() */ + #include #include #include @@ -196,6 +198,96 @@ grub_claim_heap (void) #else /* Helpers for mm on powerpc. */ +/* ibm,kernel-dump data structures */ +struct kd_section +{ + grub_uint32_t flags; + grub_uint16_t src_datatype; +#define KD_SRC_DATATYPE_REAL_MODE_REGION 0x0011 + grub_uint16_t error_flags; + grub_uint64_t src_address; + grub_uint64_t num_bytes; + grub_uint64_t act_bytes; + grub_uint64_t dst_address; +} GRUB_PACKED; + +#define MAX_KD_SECTIONS 10 + +struct kernel_dump +{ + grub_uint32_t format; + grub_uint16_t num_sections; + grub_uint16_t status_flags; + grub_uint32_t offset_1st_section; + grub_uint32_t num_blocks; + grub_uint64_t start_block; + grub_uint64_t num_blocks_avail; + grub_uint32_t offet_path_string; + grub_uint32_t max_time_allowed; + struct kd_section kds[MAX_KD_SECTIONS]; /* offset_1st_section should point to kds[0] */ +} GRUB_PACKED; + +/* + * Determine if a kernel dump exists and if it does, then determine the highest + * address that grub can use for memory allocations. + * The caller must have initialized *highest to rmo_top. *highest will not + * be modified if no kernel dump is found. + */ +static void +check_kernel_dump (grub_uint64_t *highest) +{ + struct kernel_dump kernel_dump; + grub_ssize_t kernel_dump_size; + grub_ieee1275_phandle_t rtas; + struct kd_section *kds; + grub_size_t i; + + /* If there's a kernel-dump it must have at least one section */ + if (grub_ieee1275_finddevice ("/rtas", &rtas) || + grub_ieee1275_get_property (rtas, "ibm,kernel-dump", &kernel_dump, + sizeof (kernel_dump), &kernel_dump_size) || + kernel_dump_size <= (grub_ssize_t) offsetof (struct kernel_dump, kds[1])) + return; + + kernel_dump_size = grub_min (kernel_dump_size, (grub_ssize_t) sizeof (kernel_dump)); + + if (grub_be_to_cpu32 (kernel_dump.format) != 1) + { + grub_printf (_("Error: ibm,kernel-dump has an unexpected format version '%u'\n"), + grub_be_to_cpu32 (kernel_dump.format)); + return; + } + + if (grub_be_to_cpu16 (kernel_dump.num_sections) > MAX_KD_SECTIONS) + { + grub_printf (_("Error: Too many kernel dump sections: %d\n"), + grub_be_to_cpu32 (kernel_dump.num_sections)); + return; + } + + for (i = 0; i < grub_be_to_cpu16 (kernel_dump.num_sections); i++) + { + kds = (struct kd_section *) ((grub_addr_t) &kernel_dump + + grub_be_to_cpu32 (kernel_dump.offset_1st_section) + + i * sizeof (struct kd_section)); + /* sanity check the address is within the 'kernel_dump' struct */ + if ((grub_addr_t) kds > (grub_addr_t) &kernel_dump + kernel_dump_size + sizeof (*kds)) + { + grub_printf (_("Error: 'kds' address beyond last available section\n")); + return; + } + + if ((grub_be_to_cpu16 (kds->src_datatype) == KD_SRC_DATATYPE_REAL_MODE_REGION) && + (grub_be_to_cpu64 (kds->src_address) == 0)) + { + *highest = grub_min (*highest, grub_be_to_cpu64 (kds->num_bytes)); + break; + } + } + + return; +} + /* * How much memory does OF believe exists in total? * @@ -275,10 +367,31 @@ regions_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, * * Finally, we also want to make sure that when grub loads the kernel, * it isn't going to use up all the memory we're trying to reserve! So - * enforce our entire RUNTIME_MIN_SPACE here: + * enforce our entire RUNTIME_MIN_SPACE here (no fadump): + * + * | Top of memory == upper_mem_limit -| + * | | + * | available | + * | | + * |---------- 768 MB ----------| + * | | + * | reserved | + * | | + * |--- 768 MB - runtime min space ---| + * | | + * | available | + * | | + * |---------- 0 MB ----------| + * + * In case fadump is used, we allow the following: * * |---------- Top of memory ----------| * | | + * | unavailable | + * | (kernel dump area) | + * | | + * |--------- upper_mem_limit ---------| + * | | * | available | * | | * |---------- 768 MB ----------| @@ -333,17 +446,44 @@ regions_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, } else { + grub_uint64_t upper_mem_limit = rmo_top; + grub_uint64_t orig_addr = addr; + + check_kernel_dump (&upper_mem_limit); + /* * we order these cases to prefer higher addresses and avoid some * splitting issues + * The following shows the order of variables: + * no kernel dump: linux_rmo_save < RMO_ADDR_MAX <= upper_mem_limit == rmo_top + * with kernel dump: liuxx_rmo_save < RMO_ADDR_MAX <= upper_mem_limit <= rmo_top */ - if (addr < RMO_ADDR_MAX && (addr + len) > RMO_ADDR_MAX) + if (addr < RMO_ADDR_MAX && (addr + len) > RMO_ADDR_MAX && upper_mem_limit >= RMO_ADDR_MAX) { grub_dprintf ("ieee1275", "adjusting region for RUNTIME_MIN_SPACE: (%llx -> %llx) -> (%llx -> %llx)\n", addr, addr + len, RMO_ADDR_MAX, addr + len); len = (addr + len) - RMO_ADDR_MAX; addr = RMO_ADDR_MAX; + + /* We must not exceed the upper_mem_limit (assuming it's >= RMO_ADDR_MAX) */ + if (addr + len > upper_mem_limit) + { + /* take the bigger chunk from either below linux_rmo_save or above upper_mem_limit */ + len = upper_mem_limit - addr; + if (orig_addr < linux_rmo_save && linux_rmo_save - orig_addr > len) + { + /* lower part is bigger */ + addr = orig_addr; + len = linux_rmo_save - addr; + } + + grub_dprintf ("ieee1275", "re-adjusted region to: (%llx -> %llx)\n", + addr, addr + len); + + if (len == 0) + return 0; + } } else if ((addr < linux_rmo_save) && ((addr + len) > linux_rmo_save)) { From 3c7e842571a79ecc69ee3871849a95d202161c78 Mon Sep 17 00:00:00 2001 From: Fabian Vogt Date: Thu, 5 Oct 2023 15:57:44 +0200 Subject: [PATCH 054/402] fs/btrfs: Zero file data not backed by extents Implicit holes in file data need to be zeroed explicitly, instead of just leaving the data in the buffer uninitialized. This led to kernels randomly failing to boot in "fun" ways when loaded from btrfs with the no_holes feature enabled, because large blocks of zeros in the kernel file contained random data instead. Signed-off-by: Fabian Vogt Reviewed-by: Daniel Kiper Reviewed-by: Qu Wenruo --- grub-core/fs/btrfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 19bff4610..ba0c58352 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -1603,6 +1603,8 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data, csize = grub_le_to_cpu64 (key_out.offset) - pos; if (csize > len) csize = len; + + grub_memset (buf, 0, csize); buf += csize; pos += csize; len -= csize; From cab04dcda34a9419c7367279c2f309d4becde184 Mon Sep 17 00:00:00 2001 From: ValdikSS Date: Fri, 6 Oct 2023 20:13:51 +0300 Subject: [PATCH 055/402] kern/i386/pc/init: Flush cache only on VIA C3 and earlier The code flushes the cache on VIA processors unconditionally which is excessive. Check for cpuid family and execute wbinvd only on C3 and earlier. Fixes: https://savannah.gnu.org/bugs/?45149 Fixes: 25492a0f0 (Add wbinvd around bios call.) Signed-off-by: ValdikSS Reviewed-by: Daniel Kiper --- grub-core/kern/i386/pc/init.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/grub-core/kern/i386/pc/init.c b/grub-core/kern/i386/pc/init.c index 27bc68b8a..326d491c5 100644 --- a/grub-core/kern/i386/pc/init.c +++ b/grub-core/kern/i386/pc/init.c @@ -191,7 +191,7 @@ extern grub_uint16_t grub_bios_via_workaround1, grub_bios_via_workaround2; static void grub_via_workaround_init (void) { - grub_uint32_t manufacturer[3], max_cpuid; + grub_uint32_t manufacturer[3], max_cpuid, proc_info; if (! grub_cpu_is_cpuid_supported ()) return; @@ -200,6 +200,15 @@ grub_via_workaround_init (void) if (grub_memcmp (manufacturer, "CentaurHauls", 12) != 0) return; + if (max_cpuid > 0) + { + grub_cpuid (1, proc_info, /* Don't care. */ manufacturer[0], + manufacturer[2], manufacturer[1]); + /* Check model, apply only to VIA C3 and lower. */ + if (((proc_info & 0xf0) >> 4 | (proc_info & 0xf0000) >> 12) > 10) + return; + } + grub_bios_via_workaround1 = 0x090f; grub_bios_via_workaround2 = 0x090f; asm volatile ("wbinvd"); From 4266fd2bb2f2872a442c2565aba7f7835c50fa2a Mon Sep 17 00:00:00 2001 From: ValdikSS Date: Tue, 10 Oct 2023 21:28:44 +0300 Subject: [PATCH 056/402] disk/i386/pc/biosdisk: Read up to 63 sectors in LBA mode Current code imposes limitations on the amount of sectors read in a single call according to CHS layout of the disk even in LBA read mode. There's no need to obey CHS layout restrictions for LBA reads on LBA disks. It only slows down booting process. See: https://lore.kernel.org/grub-devel/d42a11fa-2a59-b5e7-08b1-d2c60444bb99@valdikss.org.ru/ Signed-off-by: ValdikSS Reviewed-by: Daniel Kiper --- grub-core/disk/i386/pc/biosdisk.c | 5 ++++- include/grub/disk.h | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/grub-core/disk/i386/pc/biosdisk.c b/grub-core/disk/i386/pc/biosdisk.c index f9362d17c..1d6788950 100644 --- a/grub-core/disk/i386/pc/biosdisk.c +++ b/grub-core/disk/i386/pc/biosdisk.c @@ -471,7 +471,7 @@ grub_biosdisk_rw (int cmd, grub_disk_t disk, struct grub_biosdisk_dap *dap; dap = (struct grub_biosdisk_dap *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR - + (data->sectors + + (GRUB_DISK_MAX_LBA_SECTORS << disk->log_sector_size)); dap->length = sizeof (*dap); dap->reserved = 0; @@ -561,6 +561,9 @@ get_safe_sectors (grub_disk_t disk, grub_disk_addr_t sector) struct grub_biosdisk_data *data = disk->data; grub_uint32_t sectors = data->sectors; + if (data->flags & GRUB_BIOSDISK_FLAG_LBA) + sectors = GRUB_DISK_MAX_LBA_SECTORS; + /* OFFSET = SECTOR % SECTORS */ grub_divmod64 (sector, sectors, &offset); diff --git a/include/grub/disk.h b/include/grub/disk.h index a4b5d13f3..fbf23df7f 100644 --- a/include/grub/disk.h +++ b/include/grub/disk.h @@ -190,6 +190,9 @@ typedef struct grub_disk_memberlist *grub_disk_memberlist_t; #define GRUB_DISK_MAX_MAX_AGGLOMERATE ((1 << (30 - GRUB_DISK_CACHE_BITS - GRUB_DISK_SECTOR_BITS)) - 1) +/* Maximum number of sectors to read in LBA mode at once. */ +#define GRUB_DISK_MAX_LBA_SECTORS 63 + /* Return value of grub_disk_native_sectors() in case disk size is unknown. */ #define GRUB_DISK_SIZE_UNKNOWN 0xffffffffffffffffULL From 4e10213de66aae53c9292038ff6b8b6ae3d1ed6a Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 6 Oct 2023 20:23:53 +0200 Subject: [PATCH 057/402] Revert "zfsinfo: Correct a check for error allocating memory" Original commit is wrong because grub_file_get_device_name() may return NULL if we use implicit $root. Additionally, the grub_errno is guaranteed to be GRUB_ERR_NONE at the beginning of a command. So, everything should work as expected and Coverity report, CID 73668, WRT to this code should be treated as false positive. This reverts commit 7aab03418 (zfsinfo: Correct a check for error allocating memory). Fixes: 7aab03418 (zfsinfo: Correct a check for error allocating memory) Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/fs/zfs/zfsinfo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/fs/zfs/zfsinfo.c b/grub-core/fs/zfs/zfsinfo.c index bf2918018..c8a28acf5 100644 --- a/grub-core/fs/zfs/zfsinfo.c +++ b/grub-core/fs/zfs/zfsinfo.c @@ -358,8 +358,8 @@ grub_cmd_zfs_bootfs (grub_command_t cmd __attribute__ ((unused)), int argc, return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); devname = grub_file_get_device_name (args[0]); - if (devname == NULL) - return GRUB_ERR_OUT_OF_MEMORY; + if (grub_errno) + return grub_errno; dev = grub_device_open (devname); grub_free (devname); From ad7fb8e2e02bb1dd0475ead9919c1c82514d2ef8 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Thu, 28 Sep 2023 22:33:44 +0000 Subject: [PATCH 058/402] fs/xfs: Incorrect short form directory data boundary check After parsing of the current entry, the entry pointer is advanced to the next entry at the end of the "for" loop. In case where the last entry is at the end of the data boundary, the advanced entry pointer can point off the data boundary. The subsequent boundary check for the advanced entry pointer can cause a failure. The fix is to include the boundary check into the "for" loop condition. Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper Tested-by: Sebastian Andrzej Siewior Tested-by: Marta Lewandowska --- grub-core/fs/xfs.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index b91cd32b4..ebf962793 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -810,7 +810,8 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, if (iterate_dir_call_hook (parent, "..", &ctx)) return 1; - for (i = 0; i < head->count; i++) + for (i = 0; i < head->count && + (grub_uint8_t *) de < ((grub_uint8_t *) dir + grub_xfs_fshelp_size (dir->data)); i++) { grub_uint64_t ino; grub_uint8_t *inopos = grub_xfs_inline_de_inopos(dir->data, de); @@ -845,10 +846,6 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, de->name[de->len] = c; de = grub_xfs_inline_next_de(dir->data, head, de); - - if ((grub_uint8_t *) de >= (grub_uint8_t *) dir + grub_xfs_fshelp_size (dir->data)) - return grub_error (GRUB_ERR_BAD_FS, "invalid XFS directory entry"); - } break; } From 07318ee7e11a00b9c1dea4c6b4edf62af35a511a Mon Sep 17 00:00:00 2001 From: Jon DeVree Date: Tue, 17 Oct 2023 23:03:47 -0400 Subject: [PATCH 059/402] fs/xfs: Fix XFS directory extent parsing The XFS directory entry parsing code has never been completely correct for extent based directories. The parser correctly handles the case where the directory is contained in a single extent, but then mistakenly assumes the data blocks for the multiple extent case are each identical to the single extent case. The difference in the format of the data blocks between the two cases is tiny enough that its gone unnoticed for a very long time. A recent change introduced some additional bounds checking into the XFS parser. Like GRUB's existing parser, it is correct for the single extent case but incorrect for the multiple extent case. When parsing a directory with multiple extents, this new bounds checking is sometimes (but not always) tripped and triggers an "invalid XFS directory entry" error. This probably would have continued to go unnoticed but the /boot/grub/ directory is large enough that it often has multiple extents. The difference between the two cases is that when there are multiple extents, the data blocks do not contain a trailer nor do they contain any leaf information. That information is stored in a separate set of extents dedicated to just the leaf information. These extents come after the directory entry extents and are not included in the inode size. So the existing parser already ignores the leaf extents. The only reason to read the trailer/leaf information at all is so that the parser can avoid misinterpreting that data as directory entries. So this updates the parser as follows: For the single extent case the parser doesn't change much: 1. Read the size of the leaf information from the trailer 2. Set the end pointer for the parser to the start of the leaf information. (The previous bounds checking set the end pointer to the start of the trailer, so this is actually a small improvement.) 3. Set the entries variable to the expected number of directory entries. For the multiple extent case: 1. Set the end pointer to the end of the block. 2. Do not set up the entries variable. Figuring out how many entries are in each individual block is complex and does not seem worth it when it appears to be safe to just iterate over the entire block. The bounds check itself was also dependent upon the faulty XFS parser because it accidentally used "filename + length - 1". Presumably this was able to pass the fuzzer because in the old parser there was always 8 bytes of slack space between the tail pointer and the actual end of the block. Since this is no longer the case the bounds check needs to be updated to "filename + length + 1" in order to prevent a regression in the handling of corrupt fliesystems. Notes: * When there is only one extent there will only ever be one block. If more than one block is required then XFS will always switch to holding leaf information in a separate extent. * B-tree based directories seems to be parsed properly by the same code that handles multiple extents. This is unlikely to ever occur within /boot though because its only used when there are an extremely large number of directory entries. Fixes: ef7850c75 (fs/xfs: Fix issues found while fuzzing the XFS filesystem) Fixes: b2499b29c (Adds support for the XFS filesystem.) Fixes: https://savannah.gnu.org/bugs/?64376 Signed-off-by: Jon DeVree Reviewed-by: Daniel Kiper Tested-by: Sebastian Andrzej Siewior Tested-by: Marta Lewandowska --- grub-core/fs/xfs.c | 52 +++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index ebf962793..18edfcff4 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -223,6 +223,12 @@ struct grub_xfs_inode /* Size of struct grub_xfs_inode v2, up to unused4 member included. */ #define XFS_V2_INODE_SIZE (XFS_V3_INODE_SIZE - 76) +struct grub_xfs_dir_leaf_entry +{ + grub_uint32_t hashval; + grub_uint32_t address; +} GRUB_PACKED; + struct grub_xfs_dirblock_tail { grub_uint32_t leaf_count; @@ -874,9 +880,8 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, { struct grub_xfs_dir2_entry *direntry = grub_xfs_first_de(dir->data, dirblock); - int entries; - struct grub_xfs_dirblock_tail *tail = - grub_xfs_dir_tail(dir->data, dirblock); + int entries = -1; + char *end = dirblock + dirblk_size; numread = grub_xfs_read_file (dir, 0, 0, blk << dirblk_log2, @@ -887,14 +892,27 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, return 0; } - entries = (grub_be_to_cpu32 (tail->leaf_count) - - grub_be_to_cpu32 (tail->leaf_stale)); + /* + * Leaf and tail information are only in the data block if the number + * of extents is 1. + */ + if (dir->inode.nextents == grub_cpu_to_be32_compile_time (1)) + { + struct grub_xfs_dirblock_tail *tail = grub_xfs_dir_tail (dir->data, dirblock); - if (!entries) - continue; + end = (char *) tail; + + /* Subtract the space used by leaf nodes. */ + end -= grub_be_to_cpu32 (tail->leaf_count) * sizeof (struct grub_xfs_dir_leaf_entry); + + entries = grub_be_to_cpu32 (tail->leaf_count) - grub_be_to_cpu32 (tail->leaf_stale); + + if (!entries) + continue; + } /* Iterate over all entries within this block. */ - while ((char *)direntry < (char *)tail) + while ((char *) direntry < (char *) end) { grub_uint8_t *freetag; char *filename; @@ -914,7 +932,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, } filename = (char *)(direntry + 1); - if (filename + direntry->len - 1 > (char *) tail) + if (filename + direntry->len + 1 > (char *) end) return grub_error (GRUB_ERR_BAD_FS, "invalid XFS directory entry"); /* The byte after the filename is for the filetype, padding, or @@ -928,11 +946,17 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, return 1; } - /* Check if last direntry in this block is - reached. */ - entries--; - if (!entries) - break; + /* + * The expected number of directory entries is only tracked for the + * single extent case. + */ + if (dir->inode.nextents == grub_cpu_to_be32_compile_time (1)) + { + /* Check if last direntry in this block is reached. */ + entries--; + if (!entries) + break; + } /* Select the next directory entry. */ direntry = grub_xfs_next_de(dir->data, direntry); From 2d6631d2afef9ca7a4da54f59e7c88c307bce351 Mon Sep 17 00:00:00 2001 From: Mads Kiilerich Date: Thu, 19 Oct 2023 15:22:51 +0200 Subject: [PATCH 060/402] configure: Make the Unifont FONT_SOURCE configurable with --with-unifont=FILE Font might be located in different location, the default font might not be available on all systems or other font might be preferred. Signed-off-by: Mads Kiilerich Signed-off-by: Richard Marko Reviewed-by: Daniel Kiper --- configure.ac | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/configure.ac b/configure.ac index da9c8d560..7b8c3698c 100644 --- a/configure.ac +++ b/configure.ac @@ -1832,21 +1832,28 @@ fi AC_SUBST([DJVU_FONT_SOURCE]) -FONT_SOURCE= +AC_ARG_WITH([unifont], + AS_HELP_STRING([--with-unifont=FILE], + [set the unifont source [[guessed]]])) -for ext in pcf pcf.gz bdf bdf.gz ttf ttf.gz; do - for dir in . /usr/src /usr/share/fonts/X11/misc /usr/share/fonts/unifont /usr/share/fonts/uni /usr/share/fonts/truetype/unifont /usr/share/fonts/misc; do - if test -f "$dir/unifont.$ext"; then - md5="$(md5sum "$dir/unifont.$ext"|awk '{ print $1; }')" - # PCF and BDF from version 6.3 isn't hanled properly by libfreetype. - if test "$md5" = 0a54834d2788c83886a3e1785a6a1e61 || test "$md5" = 28f2565c7a41d8d407e2551159385edb || test "$md5" = dae5e588461b3b92b87b6ffee734f936 || test "$md5" = 4a3d687aa5bb329ed05f4263a1016791 ; then - continue +if test "x$with_unifont" = x; then + # search in well-known directories + for ext in pcf pcf.gz bdf bdf.gz ttf ttf.gz; do + for dir in . /usr/src /usr/share/fonts/X11/misc /usr/share/fonts/unifont /usr/share/fonts/uni /usr/share/fonts/truetype/unifont /usr/share/fonts/misc; do + if test -f "$dir/unifont.$ext"; then + md5="$(md5sum "$dir/unifont.$ext"|awk '{ print $1; }')" + # PCF and BDF from version 6.3 isn't hanled properly by libfreetype. + if test "$md5" = 0a54834d2788c83886a3e1785a6a1e61 || test "$md5" = 28f2565c7a41d8d407e2551159385edb || test "$md5" = dae5e588461b3b92b87b6ffee734f936 || test "$md5" = 4a3d687aa5bb329ed05f4263a1016791 ; then + continue + fi + FONT_SOURCE="$dir/unifont.$ext" + break 2 fi - FONT_SOURCE="$dir/unifont.$ext" - break 2 - fi + done done -done +else + FONT_SOURCE="$with_unifont" +fi if test x"$enable_build_grub_mkfont" = xno ; then FONT_SOURCE= From 85e40b36ed454ef39194e39a13dd138b138909c4 Mon Sep 17 00:00:00 2001 From: Richard Marko Date: Thu, 19 Oct 2023 15:22:52 +0200 Subject: [PATCH 061/402] configure: Make the DJVU_FONT_SOURCE configurable with --with-dejavufont=FILE Font might be located in different location, the default font might not be available on all systems or other font might be preferred. Signed-off-by: Richard Marko Reviewed-by: Daniel Kiper --- configure.ac | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/configure.ac b/configure.ac index 7b8c3698c..c19779c14 100644 --- a/configure.ac +++ b/configure.ac @@ -1796,8 +1796,6 @@ CPPFLAGS="$SAVED_CPPFLAGS" LDFLAGS="$SAVED_LDFLAGS" -DJVU_FONT_SOURCE= - starfield_excuse= AC_ARG_ENABLE([grub-themes], @@ -1811,19 +1809,28 @@ if test x"$starfield_excuse" = x && test x"$enable_build_grub_mkfont" = xno ; th starfield_excuse="No build-time grub-mkfont" fi -if test x"$starfield_excuse" = x; then - for ext in pcf pcf.gz bdf bdf.gz ttf ttf.gz; do - for dir in . /usr/src /usr/share/fonts/X11/misc /usr/share/fonts/truetype/ttf-dejavu /usr/share/fonts/dejavu /usr/share/fonts/truetype; do - if test -f "$dir/DejaVuSans.$ext"; then - DJVU_FONT_SOURCE="$dir/DejaVuSans.$ext" - break 2 - fi - done - done +AC_ARG_WITH([dejavufont], + AS_HELP_STRING([--with-dejavufont=FILE], + [set the DejeVu source [[guessed]]])) - if test "x$DJVU_FONT_SOURCE" = x; then - starfield_excuse="No DejaVu found" - fi +if test "x$with_dejavufont" = x; then + # search in well-known directories + if test x"$starfield_excuse" = x; then + for ext in pcf pcf.gz bdf bdf.gz ttf ttf.gz; do + for dir in . /usr/src /usr/share/fonts/X11/misc /usr/share/fonts/truetype/ttf-dejavu /usr/share/fonts/dejavu /usr/share/fonts/truetype; do + if test -f "$dir/DejaVuSans.$ext"; then + DJVU_FONT_SOURCE="$dir/DejaVuSans.$ext" + break 2 + fi + done + done + + if test "x$DJVU_FONT_SOURCE" = x; then + starfield_excuse="No DejaVu found" + fi + fi +else + DJVU_FONT_SOURCE="$with_dejavufont" fi if test x"$enable_grub_themes" = xyes && test x"$starfield_excuse" != x; then From 3f79e3b158bc4aeef94220db676071cfe69e8a5f Mon Sep 17 00:00:00 2001 From: Qiumiao Zhang Date: Wed, 25 Oct 2023 11:54:57 +0800 Subject: [PATCH 062/402] util/grub-mount: Check file path sanity The function argp_parser() in util/grub-mount.c lacks a check on the sanity of the file path when parsing parameters. This results in a segmentation fault if a partition is mounted to a non-existent path. Signed-off-by: Qiumiao Zhang Reviewed-by: Daniel Kiper --- util/grub-mount.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/grub-mount.c b/util/grub-mount.c index c69889df9..bf4c8b891 100644 --- a/util/grub-mount.c +++ b/util/grub-mount.c @@ -563,6 +563,8 @@ argp_parser (int key, char *arg, struct argp_state *state) images = xrealloc (images, (num_disks + 1) * sizeof (images[0])); images[num_disks] = grub_canonicalize_file_name (arg); + if (images[num_disks] == NULL) + grub_util_error (_("cannot find `%s': %s"), arg, strerror (errno)); num_disks++; return 0; From a964e359bcc9ce299dd380c0941a588e34f00350 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Sun, 13 Aug 2023 09:19:02 +0200 Subject: [PATCH 063/402] types: Fix typo Just a small grammar mistake. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- include/grub/types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/grub/types.h b/include/grub/types.h index 0d96006fe..45536a661 100644 --- a/include/grub/types.h +++ b/include/grub/types.h @@ -349,7 +349,7 @@ static inline void grub_set_unaligned64 (void *ptr, grub_uint64_t val) /* * The grub_absolute_pointer() macro borrows the idea from Linux kernel of using * RELOC_HIDE() macro to stop GCC from checking the result of pointer arithmetic - * and also it's conversion to be inside the symbol's boundary [1]. The check + * and also its conversion to be inside the symbol's boundary [1]. The check * is sometimes false positive, especially it is controversial to emit the array * bounds [-Warray-bounds] warning on all hardwired literal pointers since GCC * 11/12 [2]. Unless a good solution can be settled, for the time being we From c6cf807fc0837625e1bf769fd4585ae70e8b8656 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Sun, 13 Aug 2023 09:15:54 +0200 Subject: [PATCH 064/402] lsefi: Add missing static qualifier known_protocols isn't used anywhere else and even misses grub_ prefix, so let's make it local (static). Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/commands/efi/lsefi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/commands/efi/lsefi.c b/grub-core/commands/efi/lsefi.c index 05e2e511e..95cba5baf 100644 --- a/grub-core/commands/efi/lsefi.c +++ b/grub-core/commands/efi/lsefi.c @@ -29,7 +29,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); -struct known_protocol +static struct known_protocol { grub_guid_t guid; const char *name; From 7ad30299da5f3ad7d74db45da0632cec419007ba Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Sun, 13 Aug 2023 09:15:02 +0200 Subject: [PATCH 065/402] efi: Deduplicate configuration table search function We do table search in many places doing exactly the same algorithm. The only minor variance in users is which table is used if several entries are present. As specification mandates uniqueness and even if it ever isn't, first entry is good enough, unify this code and always use the first entry. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/commands/efi/loadbios.c | 35 +++++++------------------------ grub-core/commands/efi/lssal.c | 18 ++++++---------- grub-core/commands/efi/smbios.c | 28 ++----------------------- grub-core/kern/efi/acpi.c | 26 ++--------------------- grub-core/kern/efi/efi.c | 18 ++++++++++++++++ grub-core/kern/efi/fdt.c | 20 ++++++------------ include/grub/efi/efi.h | 3 +++ 7 files changed, 45 insertions(+), 103 deletions(-) diff --git a/grub-core/commands/efi/loadbios.c b/grub-core/commands/efi/loadbios.c index 8f6b0ecfc..8e042095a 100644 --- a/grub-core/commands/efi/loadbios.c +++ b/grub-core/commands/efi/loadbios.c @@ -92,7 +92,6 @@ lock_rom_area (void) static void fake_bios_data (int use_rom) { - unsigned i; void *acpi, *smbios; grub_uint16_t *ebda_seg_ptr, *low_mem_ptr; @@ -101,33 +100,15 @@ fake_bios_data (int use_rom) if ((*ebda_seg_ptr) || (*low_mem_ptr)) return; - acpi = 0; - smbios = 0; - for (i = 0; i < grub_efi_system_table->num_table_entries; i++) - { - grub_guid_t *guid = - &grub_efi_system_table->configuration_table[i].vendor_guid; + acpi = grub_efi_find_configuration_table (&acpi2_guid); + grub_dprintf ("efi", "ACPI2: %p\n", acpi); + if (!acpi) { + acpi = grub_efi_find_configuration_table (&acpi_guid); + grub_dprintf ("efi", "ACPI: %p\n", acpi); + } - if (! grub_memcmp (guid, &acpi2_guid, sizeof (grub_guid_t))) - { - acpi = grub_efi_system_table->configuration_table[i].vendor_table; - grub_dprintf ("efi", "ACPI2: %p\n", acpi); - } - else if (! grub_memcmp (guid, &acpi_guid, sizeof (grub_guid_t))) - { - void *t; - - t = grub_efi_system_table->configuration_table[i].vendor_table; - if (! acpi) - acpi = t; - grub_dprintf ("efi", "ACPI: %p\n", t); - } - else if (! grub_memcmp (guid, &smbios_guid, sizeof (grub_guid_t))) - { - smbios = grub_efi_system_table->configuration_table[i].vendor_table; - grub_dprintf ("efi", "SMBIOS: %p\n", smbios); - } - } + smbios = grub_efi_find_configuration_table (&smbios_guid); + grub_dprintf ("efi", "SMBIOS: %p\n", smbios); *ebda_seg_ptr = FAKE_EBDA_SEG; *low_mem_ptr = (FAKE_EBDA_SEG >> 6); diff --git a/grub-core/commands/efi/lssal.c b/grub-core/commands/efi/lssal.c index fd6085f1b..7248bdc29 100644 --- a/grub-core/commands/efi/lssal.c +++ b/grub-core/commands/efi/lssal.c @@ -136,22 +136,16 @@ grub_cmd_lssal (struct grub_command *cmd __attribute__ ((unused)), int argc __attribute__ ((unused)), char **args __attribute__ ((unused))) { - const grub_efi_system_table_t *st = grub_efi_system_table; - grub_efi_configuration_table_t *t = st->configuration_table; - unsigned int i; static grub_guid_t guid = GRUB_EFI_SAL_TABLE_GUID; + void *table = grub_efi_find_configuration_table (&guid); - for (i = 0; i < st->num_table_entries; i++) + if (table == NULL) { - if (grub_memcmp (&guid, &t->vendor_guid, - sizeof (grub_guid_t)) == 0) - { - disp_sal (t->vendor_table); - return GRUB_ERR_NONE; - } - t++; + grub_printf ("SAL not found\n"); + return GRUB_ERR_NONE; } - grub_printf ("SAL not found\n"); + + disp_sal (table); return GRUB_ERR_NONE; } diff --git a/grub-core/commands/efi/smbios.c b/grub-core/commands/efi/smbios.c index d77239732..717e5fc1d 100644 --- a/grub-core/commands/efi/smbios.c +++ b/grub-core/commands/efi/smbios.c @@ -18,44 +18,20 @@ */ #include -#include #include -#include struct grub_smbios_eps * grub_machine_smbios_get_eps (void) { - unsigned i; static grub_guid_t smbios_guid = GRUB_EFI_SMBIOS_TABLE_GUID; - for (i = 0; i < grub_efi_system_table->num_table_entries; i++) - { - grub_guid_t *guid = - &grub_efi_system_table->configuration_table[i].vendor_guid; - - if (! grub_memcmp (guid, &smbios_guid, sizeof (grub_guid_t))) - return (struct grub_smbios_eps *) - grub_efi_system_table->configuration_table[i].vendor_table; - } - - return 0; + return (struct grub_smbios_eps *) grub_efi_find_configuration_table (&smbios_guid); } struct grub_smbios_eps3 * grub_machine_smbios_get_eps3 (void) { - unsigned i; static grub_guid_t smbios3_guid = GRUB_EFI_SMBIOS3_TABLE_GUID; - for (i = 0; i < grub_efi_system_table->num_table_entries; i++) - { - grub_guid_t *guid = - &grub_efi_system_table->configuration_table[i].vendor_guid; - - if (! grub_memcmp (guid, &smbios3_guid, sizeof (grub_guid_t))) - return (struct grub_smbios_eps3 *) - grub_efi_system_table->configuration_table[i].vendor_table; - } - - return 0; + return (struct grub_smbios_eps3 *) grub_efi_find_configuration_table (&smbios3_guid); } diff --git a/grub-core/kern/efi/acpi.c b/grub-core/kern/efi/acpi.c index 461c77c33..828e6dbb2 100644 --- a/grub-core/kern/efi/acpi.c +++ b/grub-core/kern/efi/acpi.c @@ -18,42 +18,20 @@ */ #include -#include #include -#include struct grub_acpi_rsdp_v10 * grub_machine_acpi_get_rsdpv1 (void) { - unsigned i; static grub_guid_t acpi_guid = GRUB_EFI_ACPI_TABLE_GUID; - for (i = 0; i < grub_efi_system_table->num_table_entries; i++) - { - grub_guid_t *guid = - &grub_efi_system_table->configuration_table[i].vendor_guid; - - if (! grub_memcmp (guid, &acpi_guid, sizeof (grub_guid_t))) - return (struct grub_acpi_rsdp_v10 *) - grub_efi_system_table->configuration_table[i].vendor_table; - } - return 0; + return (struct grub_acpi_rsdp_v10 *) grub_efi_find_configuration_table (&acpi_guid); } struct grub_acpi_rsdp_v20 * grub_machine_acpi_get_rsdpv2 (void) { - unsigned i; static grub_guid_t acpi20_guid = GRUB_EFI_ACPI_20_TABLE_GUID; - for (i = 0; i < grub_efi_system_table->num_table_entries; i++) - { - grub_guid_t *guid = - &grub_efi_system_table->configuration_table[i].vendor_guid; - - if (! grub_memcmp (guid, &acpi20_guid, sizeof (grub_guid_t))) - return (struct grub_acpi_rsdp_v20 *) - grub_efi_system_table->configuration_table[i].vendor_table; - } - return 0; + return (struct grub_acpi_rsdp_v20 *) grub_efi_find_configuration_table (&acpi20_guid); } diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c index a2afd8de9..e53808307 100644 --- a/grub-core/kern/efi/efi.c +++ b/grub-core/kern/efi/efi.c @@ -1031,3 +1031,21 @@ grub_efi_compare_device_paths (const grub_efi_device_path_t *dp1, return 0; } + +void * +grub_efi_find_configuration_table (const grub_guid_t *target_guid) +{ + unsigned i; + + for (i = 0; i < grub_efi_system_table->num_table_entries; i++) + { + grub_guid_t *guid = + &grub_efi_system_table->configuration_table[i].vendor_guid; + + if (! grub_memcmp (guid, target_guid, sizeof (grub_guid_t))) + return (void *) + grub_efi_system_table->configuration_table[i].vendor_table; + } + + return 0; +} diff --git a/grub-core/kern/efi/fdt.c b/grub-core/kern/efi/fdt.c index 8fcf43f1b..15a495a34 100644 --- a/grub-core/kern/efi/fdt.c +++ b/grub-core/kern/efi/fdt.c @@ -23,21 +23,13 @@ void * grub_efi_get_firmware_fdt (void) { - grub_efi_configuration_table_t *tables; static grub_guid_t fdt_guid = GRUB_EFI_DEVICE_TREE_GUID; - void *firmware_fdt = NULL; - unsigned int i; - - /* Look for FDT in UEFI config tables. */ - tables = grub_efi_system_table->configuration_table; - - for (i = 0; i < grub_efi_system_table->num_table_entries; i++) - if (grub_memcmp (&tables[i].vendor_guid, &fdt_guid, sizeof (fdt_guid)) == 0) - { - firmware_fdt = tables[i].vendor_table; - grub_dprintf ("linux", "found registered FDT @ %p\n", firmware_fdt); - break; - } + void *firmware_fdt = grub_efi_find_configuration_table (&fdt_guid); + if (firmware_fdt) { + grub_dprintf ("linux", "found registered FDT @ %p\n", firmware_fdt); + } else { + grub_dprintf ("linux", "not found registered FDT\n"); + } return firmware_fdt; } diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h index 572f7135f..a5cd99e5a 100644 --- a/include/grub/efi/efi.h +++ b/include/grub/efi/efi.h @@ -119,6 +119,9 @@ extern void (*EXPORT_VAR(grub_efi_net_config)) (grub_efi_handle_t hnd, char **device, char **path); +void * +EXPORT_FUNC (grub_efi_find_configuration_table) (const grub_guid_t *target_guid); + #if defined(__arm__) || defined(__aarch64__) || defined(__riscv) || defined(__loongarch__) void *EXPORT_FUNC(grub_efi_get_firmware_fdt)(void); grub_err_t EXPORT_FUNC(grub_efi_get_ram_base)(grub_addr_t *); From 5fc985bfdda8b04c65e3768a55412f8208bb6504 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Tue, 31 Oct 2023 18:24:09 +0100 Subject: [PATCH 066/402] gpt_partition: Mark grub_gpt_partentry as having natural alignment gpt_partition contains grub_guid. We need to decide whether the whole structure is unaligned and then we need to use packed_guid. But we never have unaligned part entries as we read them in an aligned buffer from disk. Hence just make it all aligned. Signed-off-by: Vladimir Serbinenko --- include/grub/gpt_partition.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/grub/gpt_partition.h b/include/grub/gpt_partition.h index 020e71920..292ea03f1 100644 --- a/include/grub/gpt_partition.h +++ b/include/grub/gpt_partition.h @@ -67,7 +67,7 @@ struct grub_gpt_partentry grub_uint64_t end; grub_uint64_t attrib; char name[72]; -} GRUB_PACKED; +}; grub_err_t grub_gpt_partition_map_iterate (grub_disk_t disk, From 7de6fe9635f1275268116a0c835cb207d2d0aa6f Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Sun, 13 Aug 2023 09:18:23 +0200 Subject: [PATCH 067/402] types: Split aligned and packed guids On ia64 alignment requirements are strict. When we pass a pointer to UUID it needs to be at least 4-byte aligned or EFI will crash. On the other hand in device path there is no padding for UUID, so we need 2 types in one formor another. Make 4-byte aligned and unaligned types The code is structured in a way to accept unaligned inputs in most cases and supply 4-byte aligned outputs. Efiemu case is a bit ugly because there inputs and outputs are reversed and so we need careful casts to account for this inversion. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/commands/efi/lsefi.c | 2 +- grub-core/efiemu/runtime/efiemu.c | 41 +++++++++++++++++++++++-------- grub-core/kern/efi/efi.c | 2 +- grub-core/kern/misc.c | 2 +- grub-core/loader/i386/xnu.c | 2 +- include/grub/efi/api.h | 12 ++++----- include/grub/efiemu/efiemu.h | 4 +-- include/grub/efiemu/runtime.h | 2 +- include/grub/types.h | 11 ++++++++- 9 files changed, 54 insertions(+), 24 deletions(-) diff --git a/grub-core/commands/efi/lsefi.c b/grub-core/commands/efi/lsefi.c index 95cba5baf..7b8316d41 100644 --- a/grub-core/commands/efi/lsefi.c +++ b/grub-core/commands/efi/lsefi.c @@ -96,7 +96,7 @@ grub_cmd_lsefi (grub_command_t cmd __attribute__ ((unused)), grub_efi_handle_t handle = handles[i]; grub_efi_status_t status; grub_efi_uintn_t num_protocols; - grub_guid_t **protocols; + grub_packed_guid_t **protocols; grub_efi_device_path_t *dp; grub_printf ("Handle %p\n", handle); diff --git a/grub-core/efiemu/runtime/efiemu.c b/grub-core/efiemu/runtime/efiemu.c index c84b30652..51dc02114 100644 --- a/grub-core/efiemu/runtime/efiemu.c +++ b/grub-core/efiemu/runtime/efiemu.c @@ -66,7 +66,7 @@ efiemu_convert_pointer (grub_efi_uintn_t debug_disposition, grub_efi_status_t __grub_efi_api efiemu_get_variable (grub_efi_char16_t *variable_name, - const grub_guid_t *vendor_guid, + const grub_packed_guid_t *vendor_guid, grub_efi_uint32_t *attributes, grub_efi_uintn_t *data_size, void *data); @@ -74,11 +74,11 @@ efiemu_get_variable (grub_efi_char16_t *variable_name, grub_efi_status_t __grub_efi_api efiemu_get_next_variable_name (grub_efi_uintn_t *variable_name_size, grub_efi_char16_t *variable_name, - grub_guid_t *vendor_guid); + grub_packed_guid_t *vendor_guid); grub_efi_status_t __grub_efi_api efiemu_set_variable (grub_efi_char16_t *variable_name, - const grub_guid_t *vendor_guid, + const grub_packed_guid_t *vendor_guid, grub_efi_uint32_t attributes, grub_efi_uintn_t data_size, void *data); @@ -416,7 +416,7 @@ EFI_FUNC (efiemu_convert_pointer) (grub_efi_uintn_t debug_disposition, /* Find variable by name and GUID. */ static struct efi_variable * -find_variable (const grub_guid_t *vendor_guid, +find_variable (const grub_packed_guid_t *vendor_guid, grub_efi_char16_t *variable_name) { grub_uint8_t *ptr; @@ -438,7 +438,7 @@ find_variable (const grub_guid_t *vendor_guid, grub_efi_status_t __grub_efi_api EFI_FUNC (efiemu_get_variable) (grub_efi_char16_t *variable_name, - const grub_guid_t *vendor_guid, + const grub_packed_guid_t *vendor_guid, grub_efi_uint32_t *attributes, grub_efi_uintn_t *data_size, void *data) @@ -464,7 +464,7 @@ EFI_FUNC (efiemu_get_variable) (grub_efi_char16_t *variable_name, grub_efi_status_t __grub_efi_api EFI_FUNC (efiemu_get_next_variable_name) (grub_efi_uintn_t *variable_name_size, grub_efi_char16_t *variable_name, - grub_guid_t *vendor_guid) + grub_packed_guid_t *vendor_guid) { struct efi_variable *efivar; LOG ('l'); @@ -503,7 +503,7 @@ grub_efi_status_t __grub_efi_api EFI_FUNC grub_efi_status_t __grub_efi_api EFI_FUNC (efiemu_set_variable) (grub_efi_char16_t *variable_name, - const grub_guid_t *vendor_guid, + const grub_packed_guid_t *vendor_guid, grub_efi_uint32_t attributes, grub_efi_uintn_t data_size, void *data) @@ -597,9 +597,30 @@ struct grub_efi_runtime_services efiemu_runtime_services = .set_virtual_address_map = efiemu_set_virtual_address_map, .convert_pointer = efiemu_convert_pointer, - .get_variable = efiemu_get_variable, - .get_next_variable_name = efiemu_get_next_variable_name, - .set_variable = efiemu_set_variable, + /* + The code is structured in a way to accept unaligned inputs + in most cases and supply 4-byte aligned outputs. + + Efiemu case is a bit ugly because there inputs and outputs are + reversed and so we need careful casts to account for this + inversion. + */ + .get_variable = (grub_efi_status_t + (__grub_efi_api *) (grub_efi_char16_t *variable_name, + const grub_guid_t *vendor_guid, + grub_efi_uint32_t *attributes, + grub_efi_uintn_t *data_size, + void *data)) efiemu_get_variable, + .get_next_variable_name = (grub_efi_status_t + (__grub_efi_api *) (grub_efi_uintn_t *variable_name_size, + grub_efi_char16_t *variable_name, + grub_guid_t *vendor_guid)) efiemu_get_next_variable_name, + .set_variable = (grub_efi_status_t + (__grub_efi_api *) (grub_efi_char16_t *variable_name, + const grub_guid_t *vendor_guid, + grub_efi_uint32_t attributes, + grub_efi_uintn_t data_size, + void *data)) efiemu_set_variable, .get_next_high_monotonic_count = efiemu_get_next_high_monotonic_count, .reset_system = efiemu_reset_system diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c index e53808307..b93ae3aba 100644 --- a/grub-core/kern/efi/efi.c +++ b/grub-core/kern/efi/efi.c @@ -1039,7 +1039,7 @@ grub_efi_find_configuration_table (const grub_guid_t *target_guid) for (i = 0; i < grub_efi_system_table->num_table_entries; i++) { - grub_guid_t *guid = + grub_packed_guid_t *guid = &grub_efi_system_table->configuration_table[i].vendor_guid; if (! grub_memcmp (guid, target_guid, sizeof (grub_guid_t))) diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c index b57249acb..7cee5d75c 100644 --- a/grub-core/kern/misc.c +++ b/grub-core/kern/misc.c @@ -1068,7 +1068,7 @@ grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt0, if (*(fmt) == 'G') { ++fmt; - grub_guid_t *guid = (grub_guid_t *)(grub_addr_t) curarg; + grub_packed_guid_t *guid = (grub_packed_guid_t *)(grub_addr_t) curarg; write_number (str, &count, max_len, 8, 0, '0', 'x', guid->data1); write_char (str, &count, max_len, '-'); write_number (str, &count, max_len, 4, 0, '0', 'x', guid->data2); diff --git a/grub-core/loader/i386/xnu.c b/grub-core/loader/i386/xnu.c index 4e31ce99a..b91e2f840 100644 --- a/grub-core/loader/i386/xnu.c +++ b/grub-core/loader/i386/xnu.c @@ -694,7 +694,7 @@ grub_cpu_xnu_fill_devicetree (grub_uint64_t *fsbfreq_out) { void *ptr; struct grub_xnu_devtree_key *curkey; - grub_guid_t guid; + grub_packed_guid_t guid; char guidbuf[64]; /* Retrieve current key. */ diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index d3eaef3fb..d44d00ad7 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -730,7 +730,7 @@ typedef struct grub_efi_memory_mapped_device_path grub_efi_memory_mapped_device_ struct grub_efi_vendor_device_path { grub_efi_device_path_t header; - grub_guid_t vendor_guid; + grub_packed_guid_t vendor_guid; grub_efi_uint8_t vendor_defined_data[0]; } GRUB_PACKED; typedef struct grub_efi_vendor_device_path grub_efi_vendor_device_path_t; @@ -974,7 +974,7 @@ typedef struct grub_efi_cdrom_device_path grub_efi_cdrom_device_path_t; struct grub_efi_vendor_media_device_path { grub_efi_device_path_t header; - grub_guid_t vendor_guid; + grub_packed_guid_t vendor_guid; grub_efi_uint8_t vendor_defined_data[0]; } GRUB_PACKED; typedef struct grub_efi_vendor_media_device_path grub_efi_vendor_media_device_path_t; @@ -993,7 +993,7 @@ typedef struct grub_efi_file_path_device_path grub_efi_file_path_device_path_t; struct grub_efi_protocol_device_path { grub_efi_device_path_t header; - grub_guid_t guid; + grub_packed_guid_t guid; } GRUB_PACKED; typedef struct grub_efi_protocol_device_path grub_efi_protocol_device_path_t; @@ -1002,7 +1002,7 @@ typedef struct grub_efi_protocol_device_path grub_efi_protocol_device_path_t; struct grub_efi_piwg_device_path { grub_efi_device_path_t header; - grub_guid_t guid; + grub_packed_guid_t guid; } GRUB_PACKED; typedef struct grub_efi_piwg_device_path grub_efi_piwg_device_path_t; @@ -1287,7 +1287,7 @@ struct grub_efi_boot_services grub_efi_status_t (__grub_efi_api *protocols_per_handle) (grub_efi_handle_t handle, - grub_guid_t ***protocol_buffer, + grub_packed_guid_t ***protocol_buffer, grub_efi_uintn_t *protocol_buffer_count); grub_efi_status_t @@ -1386,7 +1386,7 @@ typedef struct grub_efi_runtime_services grub_efi_runtime_services_t; struct grub_efi_configuration_table { - grub_guid_t vendor_guid; + grub_packed_guid_t vendor_guid; void *vendor_table; } GRUB_PACKED; typedef struct grub_efi_configuration_table grub_efi_configuration_table_t; diff --git a/include/grub/efiemu/efiemu.h b/include/grub/efiemu/efiemu.h index caf0b505f..d6a868e94 100644 --- a/include/grub/efiemu/efiemu.h +++ b/include/grub/efiemu/efiemu.h @@ -183,13 +183,13 @@ struct grub_efiemu_configuration_table }; struct grub_efiemu_configuration_table32 { - grub_guid_t vendor_guid; + grub_packed_guid_t vendor_guid; grub_efi_uint32_t vendor_table; } GRUB_PACKED; typedef struct grub_efiemu_configuration_table32 grub_efiemu_configuration_table32_t; struct grub_efiemu_configuration_table64 { - grub_guid_t vendor_guid; + grub_packed_guid_t vendor_guid; grub_efi_uint64_t vendor_table; } GRUB_PACKED; typedef struct grub_efiemu_configuration_table64 grub_efiemu_configuration_table64_t; diff --git a/include/grub/efiemu/runtime.h b/include/grub/efiemu/runtime.h index c9ad9fdfa..2ff429845 100644 --- a/include/grub/efiemu/runtime.h +++ b/include/grub/efiemu/runtime.h @@ -29,7 +29,7 @@ struct grub_efiemu_ptv_rel struct efi_variable { - grub_guid_t guid; + grub_packed_guid_t guid; grub_uint32_t namelen; grub_uint32_t size; grub_efi_uint32_t attributes; diff --git a/include/grub/types.h b/include/grub/types.h index 45536a661..064066e2e 100644 --- a/include/grub/types.h +++ b/include/grub/types.h @@ -376,7 +376,16 @@ struct grub_guid grub_uint16_t data2; grub_uint16_t data3; grub_uint8_t data4[8]; -} GRUB_PACKED; +} __attribute__ ((aligned(4))); typedef struct grub_guid grub_guid_t; +struct grub_packed_guid +{ + grub_uint32_t data1; + grub_uint16_t data2; + grub_uint16_t data3; + grub_uint8_t data4[8]; +} GRUB_PACKED; +typedef struct grub_packed_guid grub_packed_guid_t; + #endif /* ! GRUB_TYPES_HEADER */ From 64e3cee72ab8f5876abfebb263b5e6cf7c4a9a4e Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Sun, 13 Aug 2023 09:18:53 +0200 Subject: [PATCH 068/402] gpt: Add compile time asserts for guid and gpt_partentry sizes With new alignment specification it's easy to screw up. Fortunately if it happens the size will be bigger than intended. Compile time assert will catch this. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/partmap/gpt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/partmap/gpt.c b/grub-core/partmap/gpt.c index 877ceefc3..426f616ae 100644 --- a/grub-core/partmap/gpt.c +++ b/grub-core/partmap/gpt.c @@ -229,6 +229,9 @@ static struct grub_partition_map grub_gpt_partition_map = GRUB_MOD_INIT(part_gpt) { + COMPILE_TIME_ASSERT(sizeof(grub_guid_t) == 16); + COMPILE_TIME_ASSERT(sizeof(grub_packed_guid_t) == 16); + COMPILE_TIME_ASSERT(sizeof(struct grub_gpt_partentry) == 128); grub_partition_map_register (&grub_gpt_partition_map); } From aa7c1322671eef48ba72d3b733da37e63eb37328 Mon Sep 17 00:00:00 2001 From: Anthony Iliopoulos Date: Thu, 26 Oct 2023 11:53:39 +0200 Subject: [PATCH 069/402] fs/xfs: Add large extent counters incompat feature support XFS introduced 64-bit extent counters for inodes via a series of upstream commits and the feature was marked as stable in v6.5 via commit 61d7e8274cd8 (xfs: drop EXPERIMENTAL tag for large extent counts). Further, xfsprogs release v6.5.0 switched this feature on by default in mkfs.xfs via commit e5b18d7d1d96 (mkfs: enable large extent counts by default). Filesystems formatted with large extent count support, nrext64=1, are thus currently not recognizable by GRUB, since this is an incompat feature. Add the required support so that those filesystems and inodes with large extent counters can be read by GRUB. Signed-off-by: Anthony Iliopoulos Reviewed-by: Andrey Albershteyn Reviewed-by: Daniel Kiper Tested-by: Marta Lewandowska Tested-by: Sebastian Andrzej Siewior --- grub-core/fs/xfs.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index 18edfcff4..bc2224dbb 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -79,6 +79,8 @@ GRUB_MOD_LICENSE ("GPLv3+"); /* Inode flags2 flags */ #define XFS_DIFLAG2_BIGTIME_BIT 3 #define XFS_DIFLAG2_BIGTIME (1 << XFS_DIFLAG2_BIGTIME_BIT) +#define XFS_DIFLAG2_NREXT64_BIT 4 +#define XFS_DIFLAG2_NREXT64 (1 << XFS_DIFLAG2_NREXT64_BIT) /* incompat feature flags */ #define XFS_SB_FEAT_INCOMPAT_FTYPE (1 << 0) /* filetype in dirent */ @@ -86,6 +88,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define XFS_SB_FEAT_INCOMPAT_META_UUID (1 << 2) /* metadata UUID */ #define XFS_SB_FEAT_INCOMPAT_BIGTIME (1 << 3) /* large timestamps */ #define XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR (1 << 4) /* needs xfs_repair */ +#define XFS_SB_FEAT_INCOMPAT_NREXT64 (1 << 5) /* large extent counters */ /* * Directory entries with ftype are explicitly handled by GRUB code. @@ -101,7 +104,8 @@ GRUB_MOD_LICENSE ("GPLv3+"); XFS_SB_FEAT_INCOMPAT_SPINODES | \ XFS_SB_FEAT_INCOMPAT_META_UUID | \ XFS_SB_FEAT_INCOMPAT_BIGTIME | \ - XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR) + XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR | \ + XFS_SB_FEAT_INCOMPAT_NREXT64) struct grub_xfs_sblock { @@ -203,7 +207,8 @@ struct grub_xfs_inode grub_uint16_t mode; grub_uint8_t version; grub_uint8_t format; - grub_uint8_t unused2[26]; + grub_uint8_t unused2[18]; + grub_uint64_t nextents_big; grub_uint64_t atime; grub_uint64_t mtime; grub_uint64_t ctime; @@ -545,11 +550,26 @@ get_fsb (const void *keys, int idx) return grub_be_to_cpu64 (grub_get_unaligned64 (p)); } +static int +grub_xfs_inode_has_large_extent_counts (const struct grub_xfs_inode *inode) +{ + return inode->version >= 3 && + (inode->flags2 & grub_cpu_to_be64_compile_time (XFS_DIFLAG2_NREXT64)); +} + +static grub_uint64_t +grub_xfs_get_inode_nextents (struct grub_xfs_inode *inode) +{ + return (grub_xfs_inode_has_large_extent_counts (inode)) ? + grub_be_to_cpu64 (inode->nextents_big) : + grub_be_to_cpu32 (inode->nextents); +} + static grub_disk_addr_t grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) { struct grub_xfs_btree_node *leaf = 0; - int ex, nrec; + grub_uint64_t ex, nrec; struct grub_xfs_extent *exts; grub_uint64_t ret = 0; @@ -574,7 +594,7 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) / (2 * sizeof (grub_uint64_t)); do { - int i; + grub_uint64_t i; for (i = 0; i < nrec; i++) { @@ -621,7 +641,7 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) grub_addr_t exts_end = 0; grub_addr_t data_end = 0; - nrec = grub_be_to_cpu32 (node->inode.nextents); + nrec = grub_xfs_get_inode_nextents (&node->inode); exts = (struct grub_xfs_extent *) grub_xfs_inode_data(&node->inode); if (grub_mul (sizeof (struct grub_xfs_extent), nrec, &exts_end) || From 0bb59fa9a3190e785be683bf1081287a78273544 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 30 Nov 2023 09:17:15 -0500 Subject: [PATCH 070/402] kern/ieee1275/init/ppc64: Introduce a request for regions_claim() The regions_claim() function limits the allocation of memory regions by excluding certain memory areas from being used by GRUB. This for example includes a gap between 640MB and 768MB as well as an upper limit beyond which no memory may be used when an fadump is present. However, the ieee1275 loader for kernel and initrd currently does not use regions_claim() for memory allocation on PowerVM and KVM on Power and therefore may allocate memory in those areas that it should not use. To make the regions_claim() function more flexible and ultimately usable for the ieee1275 loader, introduce a request structure to pass various parameters to the regions_claim() function that describe the properties of requested memory chunks. In a first step, move the total and flags variables into this structure. Signed-off-by: Stefan Berger Reviewed-by: Daniel Kiper Cc: Hari Bathini Cc: Pavithra Prakash Cc: Michael Ellerman Cc: Carolyn Scherrer Cc: Mahesh Salgaonkar Cc: Sourabh Jain --- grub-core/Makefile.am | 2 ++ grub-core/kern/ieee1275/init.c | 43 ++++++++++++++++++++++++++-------- include/grub/ieee1275/alloc.h | 31 ++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 10 deletions(-) create mode 100644 include/grub/ieee1275/alloc.h diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index f0cb2f2cc..fc9fc6a90 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -153,6 +153,7 @@ endif if COND_i386_ieee1275 KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/ieee1275.h +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 @@ -240,6 +241,7 @@ endif if COND_powerpc_ieee1275 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/ieee1275.h +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 diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index d6c9c9049..b65fc9be5 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -46,6 +46,9 @@ #ifdef __sparc__ #include #endif +#if defined(__powerpc__) || defined(__i386__) +#include +#endif /* The maximum heap size we're going to claim at boot. Not used by sparc. */ #ifdef __i386__ @@ -317,9 +320,9 @@ count_free (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, static int regions_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, - unsigned int flags, void *data) + void *data) { - grub_uint32_t total = *(grub_uint32_t *) data; + struct regions_claim_request *rcr = data; grub_uint64_t linux_rmo_save; if (type != GRUB_MEMORY_AVAILABLE) @@ -499,11 +502,11 @@ regions_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, } } } - if (flags & GRUB_MM_ADD_REGION_CONSECUTIVE && len < total) + if (rcr->flags & GRUB_MM_ADD_REGION_CONSECUTIVE && len < rcr->total) return 0; - if (len > total) - len = total; + if (len > rcr->total) + len = rcr->total; if (len) { @@ -513,12 +516,12 @@ regions_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, if (err) return err; grub_mm_init_region ((void *) (grub_addr_t) addr, len); - total -= len; + rcr->total -= len; } - *(grub_uint32_t *) data = total; + *(grub_uint32_t *) data = rcr->total; - if (total == 0) + if (rcr->total == 0) return 1; return 0; @@ -528,14 +531,34 @@ static int heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, void *data) { - return regions_claim (addr, len, type, GRUB_MM_ADD_REGION_NONE, data); + struct regions_claim_request rcr = { + .flags = GRUB_MM_ADD_REGION_NONE, + .total = *(grub_uint32_t *) data, + }; + int ret; + + ret = regions_claim (addr, len, type, &rcr); + + *(grub_uint32_t *) data = rcr.total; + + return ret; } static int region_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, void *data) { - return regions_claim (addr, len, type, GRUB_MM_ADD_REGION_CONSECUTIVE, data); + struct regions_claim_request rcr = { + .flags = GRUB_MM_ADD_REGION_CONSECUTIVE, + .total = *(grub_uint32_t *) data, + }; + int ret; + + ret = regions_claim (addr, len, type, &rcr); + + *(grub_uint32_t *) data = rcr.total; + + return ret; } static grub_err_t diff --git a/include/grub/ieee1275/alloc.h b/include/grub/ieee1275/alloc.h new file mode 100644 index 000000000..12fade5e4 --- /dev/null +++ b/include/grub/ieee1275/alloc.h @@ -0,0 +1,31 @@ +/* alloc.h - Memory allocation for PowerVM, KVM on Power, and i386 */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2023 Free Software Foundation, Inc. + * Copyright (C) 2023 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 . + */ + +#ifndef GRUB_IEEE1275_ALLOC_HEADER +#define GRUB_IEEE1275_ALLOC_HEADER 1 + +#include + +struct regions_claim_request { + unsigned int flags; /* GRUB_MM_ADD_REGION_(NONE|CONSECUTIVE) */ + grub_uint32_t total; /* number of requested bytes */ +}; + +#endif /* GRUB_IEEE1275_ALLOC_HEADER */ From ea2c93484943e4f6e217e1768df38ce74986fc59 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 30 Nov 2023 09:17:16 -0500 Subject: [PATCH 071/402] kern/ieee1275/init/ppc64: Decide by request whether to initialize region Let the regions_claim() request structure's init_region determine whether to call grub_mm_init_region() on it. This allows for adding memory to GRUB's memory heap if init_region is set to true, or direct usage of the memory otherwise. Set all current callers' init_region to true since they want to add memory regions to GRUB's heap. Signed-off-by: Stefan Berger Reviewed-by: Daniel Kiper Cc: Hari Bathini Cc: Pavithra Prakash Cc: Michael Ellerman Cc: Carolyn Scherrer Cc: Mahesh Salgaonkar Cc: Sourabh Jain --- grub-core/kern/ieee1275/init.c | 5 ++++- include/grub/ieee1275/alloc.h | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index b65fc9be5..8e69da297 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -515,7 +515,8 @@ regions_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, err = grub_claimmap (addr, len); if (err) return err; - grub_mm_init_region ((void *) (grub_addr_t) addr, len); + if (rcr->init_region) + grub_mm_init_region ((void *) (grub_addr_t) addr, len); rcr->total -= len; } @@ -534,6 +535,7 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, struct regions_claim_request rcr = { .flags = GRUB_MM_ADD_REGION_NONE, .total = *(grub_uint32_t *) data, + .init_region = true, }; int ret; @@ -551,6 +553,7 @@ region_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, struct regions_claim_request rcr = { .flags = GRUB_MM_ADD_REGION_CONSECUTIVE, .total = *(grub_uint32_t *) data, + .init_region = true, }; int ret; diff --git a/include/grub/ieee1275/alloc.h b/include/grub/ieee1275/alloc.h index 12fade5e4..523f51fc6 100644 --- a/include/grub/ieee1275/alloc.h +++ b/include/grub/ieee1275/alloc.h @@ -21,11 +21,14 @@ #ifndef GRUB_IEEE1275_ALLOC_HEADER #define GRUB_IEEE1275_ALLOC_HEADER 1 +#include + #include struct regions_claim_request { unsigned int flags; /* GRUB_MM_ADD_REGION_(NONE|CONSECUTIVE) */ grub_uint32_t total; /* number of requested bytes */ + bool init_region; /* whether to add memory to the heap using grub_mm_init_region() */ }; #endif /* GRUB_IEEE1275_ALLOC_HEADER */ From fe5d5e85711e765199853739e7e787621804778b Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 30 Nov 2023 09:17:17 -0500 Subject: [PATCH 072/402] kern/ieee1275/init/ppc64: Return allocated address using context Return the allocated address of the memory block in the request structure if a memory allocation was actually done. Leave the address untouched otherwise. This enables a caller who wants to use the allocated memory directly, rather than adding the memory to the heap, to see where memory was allocated. None of the current callers need this but the converted ieee1275 loader will make use of it. Signed-off-by: Stefan Berger Reviewed-by: Daniel Kiper Cc: Hari Bathini Cc: Pavithra Prakash Cc: Michael Ellerman Cc: Carolyn Scherrer Cc: Mahesh Salgaonkar Cc: Sourabh Jain --- grub-core/kern/ieee1275/init.c | 2 ++ include/grub/ieee1275/alloc.h | 1 + 2 files changed, 3 insertions(+) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index 8e69da297..4b9face59 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -518,6 +518,8 @@ regions_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, if (rcr->init_region) grub_mm_init_region ((void *) (grub_addr_t) addr, len); rcr->total -= len; + + rcr->addr = addr; } *(grub_uint32_t *) data = rcr->total; diff --git a/include/grub/ieee1275/alloc.h b/include/grub/ieee1275/alloc.h index 523f51fc6..f3065ff31 100644 --- a/include/grub/ieee1275/alloc.h +++ b/include/grub/ieee1275/alloc.h @@ -29,6 +29,7 @@ struct regions_claim_request { unsigned int flags; /* GRUB_MM_ADD_REGION_(NONE|CONSECUTIVE) */ grub_uint32_t total; /* number of requested bytes */ bool init_region; /* whether to add memory to the heap using grub_mm_init_region() */ + grub_uint64_t addr; /* result address */ }; #endif /* GRUB_IEEE1275_ALLOC_HEADER */ From d49e86db2c15dd20f42262c0977f739ddf7bd6f6 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 30 Nov 2023 09:17:18 -0500 Subject: [PATCH 073/402] kern/ieee1275/init/ppc64: Add support for alignment requirements Add support for memory alignment requirements and adjust a candidate address to it before checking whether the block is large enough. This must be done in this order since the alignment adjustment can make a block smaller than what was requested. None of the current callers has memory alignment requirements but the ieee1275 loader for kernel and initrd will use it to convey them. Signed-off-by: Stefan Berger Reviewed-by: Daniel Kiper Cc: Hari Bathini Cc: Pavithra Prakash Cc: Michael Ellerman Cc: Carolyn Scherrer Cc: Mahesh Salgaonkar Cc: Sourabh Jain --- grub-core/kern/ieee1275/init.c | 14 ++++++++++++++ include/grub/ieee1275/alloc.h | 1 + 2 files changed, 15 insertions(+) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index 4b9face59..9a1243639 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -502,6 +502,20 @@ regions_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, } } } + + /* Honor alignment restrictions on candidate addr */ + if (rcr->align) + { + grub_uint64_t align_addr = ALIGN_UP (addr, rcr->align); + grub_uint64_t d = align_addr - addr; + + if (d > len) + return 0; + + len -= d; + addr = align_addr; + } + if (rcr->flags & GRUB_MM_ADD_REGION_CONSECUTIVE && len < rcr->total) return 0; diff --git a/include/grub/ieee1275/alloc.h b/include/grub/ieee1275/alloc.h index f3065ff31..e314c989d 100644 --- a/include/grub/ieee1275/alloc.h +++ b/include/grub/ieee1275/alloc.h @@ -30,6 +30,7 @@ struct regions_claim_request { grub_uint32_t total; /* number of requested bytes */ bool init_region; /* whether to add memory to the heap using grub_mm_init_region() */ grub_uint64_t addr; /* result address */ + grub_size_t align; /* alignment restrictions */ }; #endif /* GRUB_IEEE1275_ALLOC_HEADER */ From 679691a13ef583a0342888c1fcca99f663a71c68 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 30 Nov 2023 09:17:19 -0500 Subject: [PATCH 074/402] kern/ieee1275/init/ppc64: Rename regions_claim() to grub_regions_claim() Rename regions_claim() to grub_regions_claim() to make it available for memory allocation. The ieee1275 loader will use this function on PowerVM and KVM on Power and thus avoid usage of memory that it is not allowed to use. Signed-off-by: Stefan Berger Reviewed-by: Daniel Kiper Cc: Hari Bathini Cc: Pavithra Prakash Cc: Michael Ellerman Cc: Carolyn Scherrer Cc: Mahesh Salgaonkar Cc: Sourabh Jain --- grub-core/kern/ieee1275/init.c | 10 +++++----- include/grub/ieee1275/alloc.h | 3 +++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index 9a1243639..a92558e0e 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -318,9 +318,9 @@ count_free (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, return 0; } -static int -regions_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, - void *data) +int +grub_regions_claim (grub_uint64_t addr, grub_uint64_t len, + grub_memory_type_t type, void *data) { struct regions_claim_request *rcr = data; grub_uint64_t linux_rmo_save; @@ -555,7 +555,7 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, }; int ret; - ret = regions_claim (addr, len, type, &rcr); + ret = grub_regions_claim (addr, len, type, &rcr); *(grub_uint32_t *) data = rcr.total; @@ -573,7 +573,7 @@ region_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, }; int ret; - ret = regions_claim (addr, len, type, &rcr); + ret = grub_regions_claim (addr, len, type, &rcr); *(grub_uint32_t *) data = rcr.total; diff --git a/include/grub/ieee1275/alloc.h b/include/grub/ieee1275/alloc.h index e314c989d..67a785657 100644 --- a/include/grub/ieee1275/alloc.h +++ b/include/grub/ieee1275/alloc.h @@ -33,4 +33,7 @@ struct regions_claim_request { grub_size_t align; /* alignment restrictions */ }; +int EXPORT_FUNC(grub_regions_claim) (grub_uint64_t addr, grub_uint64_t len, + grub_memory_type_t type, void *data); + #endif /* GRUB_IEEE1275_ALLOC_HEADER */ From 2a9a8518e9df5cb308b3d17bc52ef85ff7be0591 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 30 Nov 2023 09:17:20 -0500 Subject: [PATCH 075/402] kern/ieee1275/cmain/ppc64: Introduce flags to identify KVM and PowerVM Introduce flags to identify PowerVM and KVM on Power and set them where each type of host has been detected. Signed-off-by: Stefan Berger Reviewed-by: Daniel Kiper Cc: Hari Bathini Cc: Pavithra Prakash Cc: Michael Ellerman Cc: Carolyn Scherrer Cc: Mahesh Salgaonkar Cc: Sourabh Jain --- grub-core/kern/ieee1275/cmain.c | 8 +++++++- include/grub/ieee1275/ieee1275.h | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/grub-core/kern/ieee1275/cmain.c b/grub-core/kern/ieee1275/cmain.c index 400d85d28..e74de3248 100644 --- a/grub-core/kern/ieee1275/cmain.c +++ b/grub-core/kern/ieee1275/cmain.c @@ -126,7 +126,10 @@ grub_ieee1275_find_options (void) #if defined(__powerpc__) if (grub_strncmp (tmp, "IBM,", 4) == 0) - grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY); + { + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY); + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_POWER_VM); + } #endif } @@ -193,6 +196,9 @@ grub_ieee1275_find_options (void) grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_PRE1_5M_CLAIM); grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_HAS_CURSORONOFF); +#if defined(__powerpc__) + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_POWER_KVM); +#endif } } diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h index 9f669fbee..dddb38514 100644 --- a/include/grub/ieee1275/ieee1275.h +++ b/include/grub/ieee1275/ieee1275.h @@ -141,6 +141,10 @@ enum grub_ieee1275_flag */ GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY, #endif + + GRUB_IEEE1275_FLAG_POWER_VM, + + GRUB_IEEE1275_FLAG_POWER_KVM, }; extern int EXPORT_FUNC(grub_ieee1275_test_flag) (enum grub_ieee1275_flag flag); From 0ac3d938a318231b0302df3e955efe0bf5053717 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 30 Nov 2023 09:17:21 -0500 Subject: [PATCH 076/402] loader/powerpc/ieee1275: Use new allocation function for kernel and initrd On PowerVM and KVM on Power use the new memory allocation function that honors restrictions on which memory GRUB can actually use. In the request structure indicate the request for a single memory block along with address alignment restrictions. Request direct usage of the memory block by setting init_region to false (prevent it from being added to GRUB's heap). Initialize the found addr to -1, so that -1 will be returned to the loader in case no memory could be allocated. Report an out-of-memory error in case the initrd could not be loaded. Signed-off-by: Stefan Berger Reviewed-by: Daniel Kiper Cc: Hari Bathini Cc: Pavithra Prakash Cc: Michael Ellerman Cc: Carolyn Scherrer Cc: Mahesh Salgaonkar Cc: Sourabh Jain --- grub-core/loader/powerpc/ieee1275/linux.c | 55 +++++++++++++++++++---- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/grub-core/loader/powerpc/ieee1275/linux.c b/grub-core/loader/powerpc/ieee1275/linux.c index e6d071508..4864e5fb0 100644 --- a/grub-core/loader/powerpc/ieee1275/linux.c +++ b/grub-core/loader/powerpc/ieee1275/linux.c @@ -30,6 +30,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -116,6 +117,22 @@ grub_linux_claimmap_iterate (grub_addr_t target, grub_size_t size, return ctx.found_addr; } +static grub_addr_t +grub_linux_claimmap_iterate_restricted (grub_size_t size, grub_size_t align) +{ + struct regions_claim_request rcr = { + .flags = GRUB_MM_ADD_REGION_CONSECUTIVE, + .total = size, + .init_region = false, + .addr = (grub_uint64_t) -1, + .align = align, + }; + + grub_machine_mmap_iterate (grub_regions_claim, &rcr); + + return rcr.addr; +} + static grub_err_t grub_linux_boot (void) { @@ -227,10 +244,18 @@ grub_linux_load64 (grub_elf_t elf, const char *filename) offset = entry - base_addr; /* Linux's incorrectly contains a virtual address. */ - /* On some systems, firmware occupies the memory we're trying to use. - * Happily, Linux can be loaded anywhere (it relocates itself). Iterate - * until we find an open area. */ - seg_addr = grub_linux_claimmap_iterate (base_addr & ~ELF64_LOADMASK, linux_size, align); + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_POWER_VM) || + grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_POWER_KVM)) + { + seg_addr = grub_linux_claimmap_iterate_restricted (linux_size, align); + } + else + { + /* On some systems, firmware occupies the memory we're trying to use. + * Happily, Linux can be loaded anywhere (it relocates itself). Iterate + * until we find an open area. */ + seg_addr = grub_linux_claimmap_iterate (base_addr & ~ELF64_LOADMASK, linux_size, align); + } if (seg_addr == (grub_addr_t) -1) return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't claim memory"); @@ -339,13 +364,25 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), size = grub_get_initrd_size (&initrd_ctx); - first_addr = linux_addr + linux_size; - /* Attempt to claim at a series of addresses until successful in - the same way that grub_rescue_cmd_linux does. */ - addr = grub_linux_claimmap_iterate (first_addr, size, 0x100000); + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_POWER_VM) || + grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_POWER_KVM)) + { + addr = grub_linux_claimmap_iterate_restricted (size, 0x100000); + } + else + { + /* Attempt to claim at a series of addresses until successful in + the same way that grub_rescue_cmd_linux does. */ + first_addr = linux_addr + linux_size; + addr = grub_linux_claimmap_iterate (first_addr, size, 0x100000); + } + if (addr == (grub_addr_t) -1) - goto fail; + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); + goto fail; + } grub_dprintf ("loader", "Loading initrd at 0x%x, size 0x%x\n", addr, size); From dc569b0777d002d92be9a7421dca763e60c528e9 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 30 Nov 2023 09:17:22 -0500 Subject: [PATCH 077/402] kern/ieee1275/ieee1275: Display successful memory claims when debugging Display successful memory claims with exact address and rounded-down MiB location and rounded-up size in MiB. Signed-off-by: Stefan Berger Reviewed-by: Daniel Kiper Cc: Eric Snowberg Cc: Hari Bathini Cc: Pavithra Prakash Cc: Michael Ellerman Cc: Carolyn Scherrer Cc: Mahesh Salgaonkar Cc: Sourabh Jain --- grub-core/kern/ieee1275/ieee1275.c | 3 +++ include/grub/powerpc/ieee1275/ieee1275.h | 3 +++ include/grub/sparc64/ieee1275/ieee1275.h | 3 +++ 3 files changed, 9 insertions(+) diff --git a/grub-core/kern/ieee1275/ieee1275.c b/grub-core/kern/ieee1275/ieee1275.c index db870db47..36ca2dbfc 100644 --- a/grub-core/kern/ieee1275/ieee1275.c +++ b/grub-core/kern/ieee1275/ieee1275.c @@ -590,6 +590,9 @@ grub_ieee1275_claim (grub_addr_t addr, grub_size_t size, unsigned int align, *result = args.base; if (args.base == IEEE1275_CELL_INVALID) return -1; + grub_dprintf ("mmap", "CLAIMED: 0x%" PRIxGRUB_IEEE1275_CELL_T " (%" + PRIuGRUB_IEEE1275_CELL_T " MiB) size: %" PRIuGRUB_SIZE " MiB\n", + args.base, args.base >> 20, ALIGN_UP (size, 1 << 20) >> 20); return 0; } diff --git a/include/grub/powerpc/ieee1275/ieee1275.h b/include/grub/powerpc/ieee1275/ieee1275.h index 3c7683fad..4eb207018 100644 --- a/include/grub/powerpc/ieee1275/ieee1275.h +++ b/include/grub/powerpc/ieee1275/ieee1275.h @@ -25,4 +25,7 @@ #define GRUB_IEEE1275_CELL_SIZEOF 4 typedef grub_uint32_t grub_ieee1275_cell_t; +#define PRIxGRUB_IEEE1275_CELL_T PRIxGRUB_UINT32_T +#define PRIuGRUB_IEEE1275_CELL_T PRIuGRUB_UINT32_T + #endif /* ! GRUB_IEEE1275_MACHINE_HEADER */ diff --git a/include/grub/sparc64/ieee1275/ieee1275.h b/include/grub/sparc64/ieee1275/ieee1275.h index 4b18468d8..ccc71aac6 100644 --- a/include/grub/sparc64/ieee1275/ieee1275.h +++ b/include/grub/sparc64/ieee1275/ieee1275.h @@ -25,6 +25,9 @@ #define GRUB_IEEE1275_CELL_SIZEOF 8 typedef grub_uint64_t grub_ieee1275_cell_t; +#define PRIxGRUB_IEEE1275_CELL_T PRIxGRUB_UINT64_T +#define PRIuGRUB_IEEE1275_CELL_T PRIuGRUB_UINT64_T + /* Encoding of 'mode' argument to grub_ieee1275_map_physical() */ #define IEEE1275_MAP_WRITE 0x0001 /* Writable */ #define IEEE1275_MAP_READ 0x0002 /* Readable */ From 5f8e091b6a668f89de4f40b15b0712bafa053cbe Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 30 Nov 2023 09:17:23 -0500 Subject: [PATCH 078/402] kern/ieee1275/init/ppc64: Fix a comment Signed-off-by: Stefan Berger Reviewed-by: Daniel Kiper --- grub-core/kern/ieee1275/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index a92558e0e..ec94ff64a 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -472,7 +472,7 @@ grub_regions_claim (grub_uint64_t addr, grub_uint64_t len, /* We must not exceed the upper_mem_limit (assuming it's >= RMO_ADDR_MAX) */ if (addr + len > upper_mem_limit) { - /* take the bigger chunk from either below linux_rmo_save or above upper_mem_limit */ + /* Take the bigger chunk from either below linux_rmo_save or above RMO_ADDR_MAX. */ len = upper_mem_limit - addr; if (orig_addr < linux_rmo_save && linux_rmo_save - orig_addr > len) { From 17c68472d1e7746a043b174afb8aab44bda109da Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Thu, 30 Nov 2023 09:17:24 -0500 Subject: [PATCH 079/402] kern/ieee1275/init/ppc64: Display upper_mem_limit when debugging Display upper_mem_limit and its rounded-down value in MiB. Signed-off-by: Stefan Berger Reviewed-by: Daniel Kiper --- grub-core/kern/ieee1275/init.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index ec94ff64a..fb7d1a3ba 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -454,6 +454,9 @@ grub_regions_claim (grub_uint64_t addr, grub_uint64_t len, check_kernel_dump (&upper_mem_limit); + grub_dprintf ("ieee1275", "upper_mem_limit is at %llx (%lld MiB)\n", + upper_mem_limit, upper_mem_limit >> 20); + /* * we order these cases to prefer higher addresses and avoid some * splitting issues From 154dcb1aea9f8fc42b2bce98bebed004d7783a7d Mon Sep 17 00:00:00 2001 From: Oliver Steffen Date: Thu, 16 Nov 2023 16:37:38 +0100 Subject: [PATCH 080/402] build: Allow explicit module dependencies The build system deduces inter-module dependencies from the symbols required and exported by the modules. This works well, except for some rare cases where the dependency is indirect or hidden. A module might not make use of any function of some other module, but still expect its functionality to be available to GRUB. To solve this, introduce a new file, currently empty, called extra_deps.lst to track these cases manually. This file gets processed in the same way as the automatically generated syminfo.lst, making it possible to inject data into the dependency resolver. Since *.lst files are set to be ignored by git, add an exception for extra_deps.lst. Additionally, introduce a new keyword for the syminfo.lst syntax: "depends" allows specifying a module dependency directly: depends ... Signed-off-by: Oliver Steffen Reviewed-by: Daniel Kiper --- .gitignore | 1 + grub-core/Makefile.am | 4 ++-- grub-core/extra_deps.lst | 0 grub-core/genmoddep.awk | 4 ++++ 4 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 grub-core/extra_deps.lst diff --git a/.gitignore b/.gitignore index 11fcecf5c..4d0dfb700 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ *.img *.log *.lst +!/grub-core/extra_deps.lst *.marker *.mod *.o diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index fc9fc6a90..a48ce37b4 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -454,8 +454,8 @@ crypto.lst: $(srcdir)/lib/libgcrypt-grub/cipher/crypto.lst platform_DATA += crypto.lst CLEANFILES += crypto.lst -syminfo.lst: gensyminfo.sh kernel_syms.lst $(MODULE_FILES) - cat kernel_syms.lst > $@.new +syminfo.lst: gensyminfo.sh kernel_syms.lst extra_deps.lst $(MODULE_FILES) + cat kernel_syms.lst extra_deps.lst > $@.new for m in $(MODULE_FILES); do \ sh $< $$m >> $@.new || exit 1; \ done diff --git a/grub-core/extra_deps.lst b/grub-core/extra_deps.lst new file mode 100644 index 000000000..e69de29bb diff --git a/grub-core/genmoddep.awk b/grub-core/genmoddep.awk index 247436392..cc987a53a 100644 --- a/grub-core/genmoddep.awk +++ b/grub-core/genmoddep.awk @@ -31,6 +31,10 @@ BEGIN { printf "%s in %s is not defined\n", $3, $2 >"/dev/stderr"; error++; } + } else if ($1 == "depends") { + for (i = 3; i <= NF; i++) { + modtab[$2] = modtab[$2] " " $i; + } } else { printf "error: %u: unrecognized input format\n", NR >"/dev/stderr"; From 57059ccb62f91548dae8550dcf8d5cc45fcee9c5 Mon Sep 17 00:00:00 2001 From: Oliver Steffen Date: Thu, 16 Nov 2023 16:37:39 +0100 Subject: [PATCH 081/402] bli: Add explicit dependency on the part_gpt module The bli module has a "hidden" dependency on the part_gpt module, which is not picked up automatically by the build system. One purpose of the bli module is to communicate the GPT UUID of the partition GRUB was launched from to Linux user-space (systemd-gpt-auto-generator). Without the part_gpt module, bli is not able to obtain the UUID. Since bli does its work in the module initialization function, the order in which the modules are loaded is also important: part_gpt needs to be loaded before the bli module. To solve this, track this dependency explicitly. Note that the Boot Loader Interface specification, which bli aims to implement, requires GPT formatted drives. The bli module ignores all other partition formats. Signed-off-by: Oliver Steffen Reviewed-by: Daniel Kiper --- grub-core/extra_deps.lst | 1 + 1 file changed, 1 insertion(+) diff --git a/grub-core/extra_deps.lst b/grub-core/extra_deps.lst index e69de29bb..f44ad6a0c 100644 --- a/grub-core/extra_deps.lst +++ b/grub-core/extra_deps.lst @@ -0,0 +1 @@ +depends bli part_gpt From e35683317ec56fc21b00700f17ad62dc599929b5 Mon Sep 17 00:00:00 2001 From: Oliver Steffen Date: Thu, 16 Nov 2023 16:37:40 +0100 Subject: [PATCH 082/402] docs: Improve bli module documentation Improve the documentation of the bli module and explain in more detail what it does. Make clear that GPT formatted drives are expected and other partition formats are ignored. Also reorder and reword this section a bit. Signed-off-by: Oliver Steffen Reviewed-by: Daniel Kiper --- docs/grub.texi | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index 975e521d1..a225f9a88 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -3880,11 +3880,20 @@ Modules can be loaded via the @command{insmod} (@pxref{insmod}) command. @section bli This module provides basic support for the Boot Loader Interface. The Boot Loader Interface specifies a set of EFI variables that are used to communicate -boot-time information between the boot loader and the operating system. +boot-time information between the bootloader and the operating system. -When loaded, the bli module publishes the GPT partition UUID of the EFI System -Partition used during boot and a string identifying GRUB as the active boot -loader, including the version number. +The following variables are placed under the vendor UUID +@code{4a67b082-0a4c-41cf-b6c7-440b29bb8c4f} when the module is loaded: + +The GPT partition UUID of the EFI System Partition used during boot is +published via the @code{LoaderDevicePartUUID} variable. The Boot Loader +Interface specification requires GPT formatted drives. The bli module +ignores drives/partitions in any other format. If GRUB is loaded from +a non-GPT partition, e.g. from an MSDOS formatted drive or network, +this variable will not be set. + +A string identifying GRUB as the active bootloader including the version +number is stored in @code{LoaderInfo}. This module is only available on UEFI platforms. From e60015f574024584e43d1b3b245551e864aa8c4d Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Tue, 21 Nov 2023 18:09:59 +0100 Subject: [PATCH 083/402] efi: Set shim_lock_enabled even if validation is disabled If validation has been disabled via MokSbState, secure boot on the firmware is still enabled, and the kernel fails to boot. This is a bit hacky, because shim_lock is not *fully* enabled, but it triggers the right code paths. Ultimately, all this will be resolved by shim gaining it's own image loading and starting protocol, so this is more a temporary workaround. Fixes: 6425c12cd (efi: Fallback to legacy mode if shim is loaded on x86 archs) Cc: Peter Jones Cc: Michael Chang Signed-off-by: Julian Andres Klode Reviewed-by: Daniel Kiper --- grub-core/kern/efi/sb.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/grub-core/kern/efi/sb.c b/grub-core/kern/efi/sb.c index 60550a6da..8d3e41360 100644 --- a/grub-core/kern/efi/sb.c +++ b/grub-core/kern/efi/sb.c @@ -95,6 +95,14 @@ grub_efi_get_secureboot (void) if (!(attr & GRUB_EFI_VARIABLE_RUNTIME_ACCESS) && *moksbstate == 1) { secureboot = GRUB_EFI_SECUREBOOT_MODE_DISABLED; + /* + * TODO: Replace this all with shim's LoadImage protocol, delegating policy to it. + * + * We need to set shim_lock_enabled here because we disabled secure boot + * validation *inside* shim but not in the firmware, so we set this variable + * here to trigger that code path, whereas the actual verifier is not enabled. + */ + shim_lock_enabled = true; goto out; } From 3f9eace2d39d94103d8961ccf1b4d8fff10f132b Mon Sep 17 00:00:00 2001 From: Mate Kukri Date: Fri, 24 Nov 2023 08:32:57 +0000 Subject: [PATCH 084/402] util/grub-install: Delay copying files to {grubdir,platdir} after install_device was validated Previously grub-install copied modules to grubdir before doing any validation on the install_device. When grub-install was called with an invalid install_device, modules were already copied to /boot before it found out and was forced to rely on atexit() rollback. This patch delays copying the modules after at least some install_device validation was done, and thus reduces reliance on successful rollback. Signed-off-by: Mate Kukri Reviewed-by: Daniel Kiper --- util/grub-install.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/util/grub-install.c b/util/grub-install.c index 1ad04db36..9feb4ea9e 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -1291,13 +1291,6 @@ main (int argc, char *argv[]) } } - grub_install_copy_files (grub_install_source_directory, - grubdir, platform); - - char *envfile = grub_util_path_concat (2, grubdir, "grubenv"); - if (!grub_util_is_regular (envfile)) - grub_util_create_envblk_file (envfile); - size_t ndev = 0; /* Write device to a variable so we don't have to traverse /dev every time. */ @@ -1384,18 +1377,7 @@ main (int argc, char *argv[]) t); free (t); } - load_cfg = grub_util_path_concat (2, platdir, - "load.cfg"); - grub_util_unlink (load_cfg); - - if (debug_image && debug_image[0]) - { - load_cfg_f = grub_util_fopen (load_cfg, "wb"); - have_load_cfg = 1; - fprintf (load_cfg_f, "set debug='%s'\n", - debug_image); - } char *prefix_drive = NULL; char *install_drive = NULL; @@ -1419,6 +1401,26 @@ main (int argc, char *argv[]) } } + grub_install_copy_files (grub_install_source_directory, + grubdir, platform); + + char *envfile = grub_util_path_concat (2, grubdir, "grubenv"); + if (!grub_util_is_regular (envfile)) + grub_util_create_envblk_file (envfile); + + load_cfg = grub_util_path_concat (2, platdir, + "load.cfg"); + + grub_util_unlink (load_cfg); + + if (debug_image && debug_image[0]) + { + load_cfg_f = grub_util_fopen (load_cfg, "wb"); + have_load_cfg = 1; + fprintf (load_cfg_f, "set debug='%s'\n", + debug_image); + } + if (!have_abstractions) { if ((disk_module && grub_strcmp (disk_module, "biosdisk") != 0) From e7a831963e30c6d9c61027a97e50388856c68d03 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Wed, 29 Nov 2023 13:42:55 +0100 Subject: [PATCH 085/402] templates: Reinstate unused version comparison functions with warning Revert the commit a79c567f6 (templates: Remove unused version comparison functions) and add a warning to the functions that they are deprecated. Removing the functions directly caused a lot of upgrade issues with custom user scripts that called the functions. In Debian and Ubuntu, grub-mkconfig is invoked as a post-installation script and would fail, causing upgrades to fail halfway through and putting the package manager into an inconsistent state. FWIW, we get one bug per 2 weeks basically, for an interim Ubuntu release which generally does not receive much usage, that is a high number. The proposal is to pick this for 2.12 and directly after the release remove it again. Then users will have time to fix their scripts without systems breaking immediately. This reverts commit a79c567f6 (templates: Remove unused version comparison functions). Cc: Mathieu Desnoyers Cc: Daniel Kiper Signed-off-by: Julian Andres Klode Reviewed-by: Daniel Kiper --- util/grub-mkconfig_lib.in | 54 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/util/grub-mkconfig_lib.in b/util/grub-mkconfig_lib.in index 08953287c..33e1750ae 100644 --- a/util/grub-mkconfig_lib.in +++ b/util/grub-mkconfig_lib.in @@ -244,6 +244,60 @@ grub_move_to_front () done } +version_test_numeric () +{ + grub_warn "version_test_numeric() is deprecated. Use version_sort() instead." + version_test_numeric_a="$1" + version_test_numeric_cmp="$2" + version_test_numeric_b="$3" + if [ "$version_test_numeric_a" = "$version_test_numeric_b" ] ; then + case "$version_test_numeric_cmp" in + ge|eq|le) return 0 ;; + gt|lt) return 1 ;; + esac + fi + if [ "$version_test_numeric_cmp" = "lt" ] ; then + version_test_numeric_c="$version_test_numeric_a" + version_test_numeric_a="$version_test_numeric_b" + version_test_numeric_b="$version_test_numeric_c" + fi + if (echo "$version_test_numeric_a" ; echo "$version_test_numeric_b") | version_sort | head -n 1 | grep -qx "$version_test_numeric_b" ; then + return 0 + else + return 1 + fi +} + +version_test_gt () +{ + grub_warn "version_test_gt() is deprecated. Use version_sort() instead." + version_test_gt_a="`echo "$1" | sed -e "s/[^-]*-//"`" + version_test_gt_b="`echo "$2" | sed -e "s/[^-]*-//"`" + version_test_gt_cmp=gt + if [ "x$version_test_gt_b" = "x" ] ; then + return 0 + fi + case "$version_test_gt_a:$version_test_gt_b" in + *.old:*.old) ;; + *.old:*) version_test_gt_a="`echo "$version_test_gt_a" | sed -e 's/\.old$//'`" ; version_test_gt_cmp=gt ;; + *:*.old) version_test_gt_b="`echo "$version_test_gt_b" | sed -e 's/\.old$//'`" ; version_test_gt_cmp=ge ;; + esac + version_test_numeric "$version_test_gt_a" "$version_test_gt_cmp" "$version_test_gt_b" + return "$?" +} + +version_find_latest () +{ + grub_warn "version_find_latest() is deprecated. Use version_sort() instead." + version_find_latest_a="" + for i in "$@" ; do + if version_test_gt "$i" "$version_find_latest_a" ; then + version_find_latest_a="$i" + fi + done + echo "$version_find_latest_a" +} + # One layer of quotation is eaten by "" and the second by sed; so this turns # ' into \'. grub_quote () { From 069cc46c9648669fc1f76a719e1526adc3fdb6c6 Mon Sep 17 00:00:00 2001 From: Mate Kukri Date: Wed, 29 Nov 2023 19:58:25 +0000 Subject: [PATCH 086/402] net/http: Fix gcc-13 errors relating to type signedness Replace definition of HTTP_PORT with a pre-processor macro that converts the constant to the correct grub_uint16_t type. Change "port" local variable definition in http_establish() to have the same type. Signed-off-by: Mate Kukri Reviewed-by: Daniel Kiper device->net->server; - int port = file->device->net->port; + grub_uint16_t port = file->device->net->port; nb = grub_netbuff_alloc (GRUB_NET_TCP_RESERVE_SIZE + sizeof ("GET ") - 1 From ed74bc3764d0807d2aa1af9ae7a22c3c1c7c7aa4 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Wed, 6 Dec 2023 11:42:03 +0800 Subject: [PATCH 087/402] util/grub-mkstandalone: Ensure stable timestamps for generated images This change mirrors a previous fix [1] but is specific to images generated by grub-mkstandalone. The former fix, commit 85a7be241 (util/mkimage: Use stable timestamp when generating binaries.), focused on utilizing a stable timestamp during binary generation in the util/mkimage context. This commit extends that approach to the images produced by grub-mkstandalone, ensuring consistency and stability in timestamps across all generated binaries. [1] 85a7be241 util/mkimage: Use stable timestamp when generating binaries. Signed-off-by: Michael Chang Signed-off-by: Bernhard Wiedemann Reviewed-by: Daniel Kiper --- util/grub-mkstandalone.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/util/grub-mkstandalone.c b/util/grub-mkstandalone.c index bdbeea6a6..8e1229925 100644 --- a/util/grub-mkstandalone.c +++ b/util/grub-mkstandalone.c @@ -30,6 +30,9 @@ #pragma GCC diagnostic error "-Wmissing-prototypes" #pragma GCC diagnostic error "-Wmissing-declarations" +/* use 2015-01-01T00:00:00+0000 as a stock timestamp */ +#define STABLE_EMBEDDING_TIMESTAMP 1420070400 + static char *output_image; static char **files; static int nfiles; @@ -184,7 +187,6 @@ add_tar_file (const char *from, struct head hd; grub_util_fd_t in; ssize_t r; - grub_uint32_t mtime = 0; grub_uint32_t size; COMPILE_TIME_ASSERT (sizeof (hd) == 512); @@ -192,8 +194,6 @@ add_tar_file (const char *from, if (grub_util_is_special_file (from)) return; - mtime = grub_util_get_mtime (from); - optr = tcn = xmalloc (strlen (to) + 1); for (iptr = to; *iptr == '/'; iptr++); for (; *iptr; iptr++) @@ -234,7 +234,7 @@ add_tar_file (const char *from, memcpy (hd.gid, "0001750", 7); set_tar_value (hd.size, optr - tcn, 12); - set_tar_value (hd.mtime, mtime, 12); + set_tar_value (hd.mtime, STABLE_EMBEDDING_TIMESTAMP, 12); hd.typeflag = 'L'; memcpy (hd.magic, MAGIC, sizeof (hd.magic)); memcpy (hd.uname, "grub", 4); @@ -264,7 +264,7 @@ add_tar_file (const char *from, memcpy (hd.gid, "0001750", 7); set_tar_value (hd.size, size, 12); - set_tar_value (hd.mtime, mtime, 12); + set_tar_value (hd.mtime, STABLE_EMBEDDING_TIMESTAMP, 12); hd.typeflag = '0'; memcpy (hd.magic, MAGIC, sizeof (hd.magic)); memcpy (hd.uname, "grub", 4); From f18a899ab17169ab71fcf3919435c4b79495efcd Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Wed, 6 Dec 2023 11:42:04 +0800 Subject: [PATCH 088/402] util/grub-mkstandalone: Ensure deterministic tar file creation by sorting contents The add_tar_files() function currently iterates through a directory's content using readdir(), which doesn't guarantee a specific order. This lack of deterministic behavior impacts reproducibility in the build process. This commit resolves the issue by introducing sorting functionality. The list retrieved by readdir() is now sorted alphabetically before incorporation into the tar archive, ensuring consistent and predictable file ordering within the archive. On the occasion fix tfp memory leak. Signed-off-by: Michael Chang Signed-off-by: Bernhard Wiedemann Reviewed-by: Daniel Kiper --- util/grub-mkstandalone.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/util/grub-mkstandalone.c b/util/grub-mkstandalone.c index 8e1229925..9009648e3 100644 --- a/util/grub-mkstandalone.c +++ b/util/grub-mkstandalone.c @@ -205,22 +205,43 @@ add_tar_file (const char *from, { grub_util_fd_dir_t d; grub_util_fd_dirent_t de; + char **from_files; + grub_size_t alloc = 8, used = 0; + grub_size_t i; d = grub_util_fd_opendir (from); + from_files = xmalloc (alloc * sizeof (*from_files)); while ((de = grub_util_fd_readdir (d))) { - char *fp, *tfp; if (strcmp (de->d_name, ".") == 0) continue; if (strcmp (de->d_name, "..") == 0) continue; - fp = grub_util_path_concat (2, from, de->d_name); - tfp = xasprintf ("%s/%s", to, de->d_name); - add_tar_file (fp, tfp); - free (fp); + if (alloc <= used) + { + alloc <<= 1; + from_files = xrealloc (from_files, alloc * sizeof (*from_files)); + } + from_files[used++] = xstrdup (de->d_name); } + + qsort (from_files, used, sizeof (*from_files), grub_qsort_strcmp); + + for (i = 0; i < used; i++) + { + char *fp, *tfp; + + fp = grub_util_path_concat (2, from, from_files[i]); + tfp = xasprintf ("%s/%s", to, from_files[i]); + add_tar_file (fp, tfp); + free (tfp); + free (fp); + free (from_files[i]); + } + grub_util_fd_closedir (d); + free (from_files); free (tcn); return; } From 353beb80c0a195d313fe3309d0436e451d1e5dad Mon Sep 17 00:00:00 2001 From: Mate Kukri Date: Fri, 8 Dec 2023 16:57:55 +0000 Subject: [PATCH 089/402] util/grub-install: Move platdir path canonicalization after files were copied to grubdir The commit 3f9eace2d (util/grub-install: Delay copying files to {grubdir,platdir} after install_device was validated) delaying copying of files caused a regression when installing without an existing directory structure. This patch ensures that the platform directory actually exists by the time the code tries to canonicalize its filename. Fixes: 3f9eace2d (util/grub-install: Delay copying files to {grubdir,platdir} after install_device was validated) Signed-off-by: Mate Kukri Reviewed-by: Daniel Kiper --- util/grub-install.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/util/grub-install.c b/util/grub-install.c index 9feb4ea9e..7dc5657bb 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -1366,18 +1366,6 @@ main (int argc, char *argv[]) relative_grubdir = xstrdup ("/"); } - char *platname = grub_install_get_platform_name (platform); - char *platdir; - { - char *t = grub_util_path_concat (2, grubdir, - platname); - platdir = grub_canonicalize_file_name (t); - if (!platdir) - grub_util_error (_("failed to get canonical path of `%s'"), - t); - free (t); - } - char *prefix_drive = NULL; char *install_drive = NULL; @@ -1408,6 +1396,18 @@ main (int argc, char *argv[]) if (!grub_util_is_regular (envfile)) grub_util_create_envblk_file (envfile); + char *platname = grub_install_get_platform_name (platform); + char *platdir; + { + char *t = grub_util_path_concat (2, grubdir, + platname); + platdir = grub_canonicalize_file_name (t); + if (!platdir) + grub_util_error (_("failed to get canonical path of `%s'"), + t); + free (t); + } + load_cfg = grub_util_path_concat (2, platdir, "load.cfg"); From 89fbe0cac97b5827941a82c55158523e5d622b42 Mon Sep 17 00:00:00 2001 From: Mate Kukri Date: Fri, 8 Dec 2023 17:20:12 +0000 Subject: [PATCH 090/402] grub-core/Makefile.am: Make path to extra_deps.lst relative to $(top_srcdir)/grub-core The commit 154dcb1ae (build: Allow explicit module dependencies) broke out of tree builds by introducing the extra_deps.lst file into the source tree but referencing it just by name in grub-core/Makefile.am. Fix it by adding $(top_srcdir)/grub-core to the path. Fixes: 154dcb1ae (build: Allow explicit module dependencies) Signed-off-by: Mate Kukri Reviewed-by: Daniel Kiper --- grub-core/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index a48ce37b4..f18550c1c 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -454,8 +454,8 @@ crypto.lst: $(srcdir)/lib/libgcrypt-grub/cipher/crypto.lst platform_DATA += crypto.lst CLEANFILES += crypto.lst -syminfo.lst: gensyminfo.sh kernel_syms.lst extra_deps.lst $(MODULE_FILES) - cat kernel_syms.lst extra_deps.lst > $@.new +syminfo.lst: gensyminfo.sh kernel_syms.lst $(top_srcdir)/grub-core/extra_deps.lst $(MODULE_FILES) + cat kernel_syms.lst $(top_srcdir)/grub-core/extra_deps.lst > $@.new for m in $(MODULE_FILES); do \ sh $< $$m >> $@.new || exit 1; \ done From c129e44e7f9af9f0a753c5490f6cc36c25aa6579 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Mon, 10 Jul 2023 17:17:35 +0200 Subject: [PATCH 091/402] loader/i386/bsdXX: Fix loading after unaligned module Current code implicitly assumes that aligning chunk_size + *kern_end is the same as aligning on curload which is not the case because chunk_size starts at zero even if *kern_end is unaligned and ALIGN_PAGE moved curload to an aligned position but not *kern_end + chunk_size. This fixes booting of FreeBSD with zfs module. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/loader/i386/bsdXX.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/grub-core/loader/i386/bsdXX.c b/grub-core/loader/i386/bsdXX.c index 09d909f1b..d3de8a29b 100644 --- a/grub-core/loader/i386/bsdXX.c +++ b/grub-core/loader/i386/bsdXX.c @@ -88,10 +88,9 @@ SUFFIX (grub_freebsd_load_elfmodule_obj) (struct grub_relocator *relocator, Elf_Shnum shnum; grub_addr_t curload, module; grub_err_t err; - grub_size_t chunk_size = 0; void *chunk_src; - curload = module = ALIGN_PAGE (*kern_end); + module = ALIGN_PAGE (*kern_end); err = read_headers (file, argv[0], &e, &shdr); if (err != GRUB_ERR_NONE) @@ -101,6 +100,8 @@ SUFFIX (grub_freebsd_load_elfmodule_obj) (struct grub_relocator *relocator, if (err != GRUB_ERR_NONE) goto out; + curload = module; + for (s = shdr; s < (Elf_Shdr *) ((grub_uint8_t *) shdr + shnum * e.e_shentsize); s = (Elf_Shdr *) ((grub_uint8_t *) s + e.e_shentsize)) { @@ -108,21 +109,24 @@ SUFFIX (grub_freebsd_load_elfmodule_obj) (struct grub_relocator *relocator, continue; if (s->sh_addralign) - chunk_size = ALIGN_UP (chunk_size + *kern_end, s->sh_addralign) - - *kern_end; + curload = ALIGN_UP (curload, s->sh_addralign); - chunk_size += s->sh_size; + curload += s->sh_size; } + *kern_end = ALIGN_PAGE (curload); + { grub_relocator_chunk_t ch; err = grub_relocator_alloc_chunk_addr (relocator, &ch, - module, chunk_size); + module, curload - module); if (err != GRUB_ERR_NONE) goto out; chunk_src = get_virtual_current_address (ch); } + curload = module; + for (s = shdr; s < (Elf_Shdr *) ((grub_uint8_t *) shdr + shnum * e.e_shentsize); s = (Elf_Shdr *) ((grub_uint8_t *) s + e.e_shentsize)) { @@ -141,13 +145,13 @@ SUFFIX (grub_freebsd_load_elfmodule_obj) (struct grub_relocator *relocator, { default: case SHT_PROGBITS: - err = load (file, argv[0], (grub_uint8_t *) chunk_src + curload - *kern_end, + err = load (file, argv[0], (grub_uint8_t *) chunk_src + curload - module, s->sh_offset, s->sh_size); if (err != GRUB_ERR_NONE) goto out; break; case SHT_NOBITS: - grub_memset ((grub_uint8_t *) chunk_src + curload - *kern_end, 0, + grub_memset ((grub_uint8_t *) chunk_src + curload - module, 0, s->sh_size); break; } From 3815acc57bd0402078693b36425b07f0d15a1695 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Thu, 24 Aug 2023 02:32:26 +0200 Subject: [PATCH 092/402] build: Tolerate unused-but-set in generated lexer/bison files We don't really control the small aspects of generated files and NetBSD version has an unused variable that is then detected by gcc as warning that is then promoted to error. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- Makefile.util.def | 2 +- grub-core/Makefile.core.def | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.util.def b/Makefile.util.def index 6fe08efd8..9432365a9 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -55,7 +55,7 @@ library = { library = { name = libgrubmods.a; - cflags = '-fno-builtin -Wno-undef'; + cflags = '-fno-builtin -Wno-undef -Wno-unused-but-set-variable'; cppflags = '-I$(srcdir)/grub-core/lib/minilzo -I$(srcdir)/grub-core/lib/xzembed -I$(srcdir)/grub-core/lib/zstd -DMINILZO_HAVE_CONFIG_H'; common_nodist = grub_script.tab.c; diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index d2cf29584..1571421d7 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1993,7 +1993,7 @@ module = { extra_dist = script/yylex.l; extra_dist = script/parser.y; - cflags = '$(CFLAGS_POSIX) -Wno-redundant-decls'; + cflags = '$(CFLAGS_POSIX) -Wno-redundant-decls -Wno-unused-but-set-variable'; cppflags = '$(CPPFLAGS_POSIX)'; }; From 2f3faf02c4e325b916306be27fa0e975a41ee167 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Thu, 24 Aug 2023 02:32:55 +0200 Subject: [PATCH 093/402] disk/diskfilter: Remove unused variable Variable e is set but never used. We can just remove it now. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/disk/diskfilter.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/grub-core/disk/diskfilter.c b/grub-core/disk/diskfilter.c index 61a311efd..21e239511 100644 --- a/grub-core/disk/diskfilter.c +++ b/grub-core/disk/diskfilter.c @@ -720,7 +720,7 @@ read_segment (struct grub_diskfilter_segment *seg, grub_disk_addr_t sector, case GRUB_DISKFILTER_RAID6: { grub_disk_addr_t read_sector; - grub_uint64_t b, p, n, disknr, e; + grub_uint64_t b, p, n, disknr; /* n = 1 for level 4 and 5, 2 for level 6. */ n = seg->type / 3; @@ -770,7 +770,6 @@ read_segment (struct grub_diskfilter_segment *seg, grub_disk_addr_t sector, if (read_size > size) read_size = size; - e = 0; /* Reset read error. */ if (grub_errno == GRUB_ERR_READ_ERROR || grub_errno == GRUB_ERR_UNKNOWN_DEVICE) @@ -784,7 +783,6 @@ read_segment (struct grub_diskfilter_segment *seg, grub_disk_addr_t sector, if ((err) && (err != GRUB_ERR_READ_ERROR && err != GRUB_ERR_UNKNOWN_DEVICE)) return err; - e++; if (err) { From cb1824a871823ae73d208640e4a89a7d7d110963 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Thu, 24 Aug 2023 09:33:47 +0200 Subject: [PATCH 094/402] osdep/generic/blocklist: Fix compilation After recent change in blocklist types we have a type mismatch. Fixing it requires a wrapper or large changes. I feel like wrapper makes more sense. Without this patch we end up with a compilation problem and without wrapping callback data is not passed properly anymore. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/osdep/generic/blocklist.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/grub-core/osdep/generic/blocklist.c b/grub-core/osdep/generic/blocklist.c index 2d9040302..9ff8e44c6 100644 --- a/grub-core/osdep/generic/blocklist.c +++ b/grub-core/osdep/generic/blocklist.c @@ -30,6 +30,26 @@ #define MAX_TRIES 5 +struct wrapper_hook_data +{ + void (*callback) (grub_disk_addr_t sector, + unsigned int offset, + unsigned int length, + void *data); + void *callback_data; +}; + +static grub_err_t +callback_wrapper (grub_disk_addr_t sector, + unsigned int offset, unsigned int length, + char *buf, void *data) +{ + struct wrapper_hook_data *wrap = data; + + wrap->callback (sector, offset, length, wrap->callback_data); + return GRUB_ERR_NONE; +} + void grub_install_get_blocklist (grub_device_t root_dev, const char *core_path, const char *core_img, @@ -43,6 +63,10 @@ grub_install_get_blocklist (grub_device_t root_dev, int i; char *tmp_img; char *core_path_dev; + struct wrapper_hook_data wrap_hook_data = { + .callback = callback, + .callback_data = hook_data + }; core_path_dev = grub_make_system_path_relative_to_its_root (core_path); @@ -120,8 +144,8 @@ grub_install_get_blocklist (grub_device_t root_dev, if (! file) grub_util_error ("%s", grub_errmsg); - file->read_hook = callback; - file->read_hook_data = hook_data; + file->read_hook = callback_wrapper; + file->read_hook_data = &wrap_hook_data; if (grub_file_read (file, tmp_img, core_size) != (grub_ssize_t) core_size) grub_util_error ("%s", _("failed to read the sectors of the core image")); From f5905f6566f239a6ebec2810dd66efa4d3ed3704 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Thu, 24 Aug 2023 21:48:20 +0200 Subject: [PATCH 095/402] osdep/bsd/hostdisk: Fix NetBSD compilation Wrong function and variable name cause a stupid compilation error on NetBSD and OpenBSD. Only NetBSD and OpenBSD use this file. No other platform is affected. Additionally, define RAW_FLOPPY_MAJOR constant if it is missing. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/osdep/bsd/hostdisk.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/grub-core/osdep/bsd/hostdisk.c b/grub-core/osdep/bsd/hostdisk.c index 891109462..1a7ea97b4 100644 --- a/grub-core/osdep/bsd/hostdisk.c +++ b/grub-core/osdep/bsd/hostdisk.c @@ -56,6 +56,10 @@ # endif #if defined(__NetBSD__) +# ifndef RAW_FLOPPY_MAJOR +# define RAW_FLOPPY_MAJOR 9 +# endif /* ! RAW_FLOPPY_MAJOR */ + /* Adjust device driver parameters. This function should be called just after successfully opening the device. For now, it simply prevents the floppy driver from retrying operations on failure, as otherwise the @@ -92,7 +96,7 @@ grub_util_fd_open (const char *os_dev, int flags) ret = open (os_dev, flags, S_IROTH | S_IRGRP | S_IRUSR | S_IWUSR); if (ret >= 0) - configure_device_driver (fd); + configure_device_driver (ret); return ret; } @@ -105,7 +109,7 @@ grub_util_get_fd_size_os (grub_util_fd_t fd, const char *name, unsigned *log_sec unsigned sector_size, log_sector_size; #if defined(__NetBSD__) - grub_hostdisk_configure_device_driver (fd); + configure_device_driver (fd); #endif if (ioctl (fd, DIOCGDINFO, &label) == -1) From 31e47cfe2c3958afd1faacf387c0bf8da33560a9 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Thu, 24 Aug 2023 22:32:45 +0200 Subject: [PATCH 096/402] util/editenv: Don't use %m formatter It's not available on NetBSD outside of syslog. Using strerror() is more reliable as we retrieve errno immediately rather than down the stack. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- util/editenv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/editenv.c b/util/editenv.c index c532b046f..63a503040 100644 --- a/util/editenv.c +++ b/util/editenv.c @@ -91,7 +91,7 @@ grub_util_create_envblk_file (const char *name) else if (retsize < 0) { free (linkbuf); - grub_util_error (_("cannot rename the file %s to %s: %m"), namenew, name); + grub_util_error (_("cannot rename the file %s to %s: %s"), namenew, name, strerror (errno)); } else if (retsize == size) { @@ -128,7 +128,7 @@ grub_util_create_envblk_file (const char *name) free (rename_target); if (rc < 0) - grub_util_error (_("cannot rename the file %s to %s: %m"), namenew, name); + grub_util_error (_("cannot rename the file %s to %s: %s"), namenew, name, strerror (errno)); #endif free (namenew); From e4dbe5cfa4592099fae71cfab4bf9caaa2a272f3 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Thu, 24 Aug 2023 22:45:53 +0200 Subject: [PATCH 097/402] gnulib: Tolerate always_inline attribute being ignored It's not critical, -Werror on it is inappropriate. We don't want to modify gnulib too much. This warning is pretty much irrelevant. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- conf/Makefile.common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/Makefile.common b/conf/Makefile.common index f8faa92e9..b8f216f6c 100644 --- a/conf/Makefile.common +++ b/conf/Makefile.common @@ -75,7 +75,7 @@ grubconfdir = $(sysconfdir)/grub.d platformdir = $(pkglibdir)/$(target_cpu)-$(platform) starfielddir = $(pkgdatadir)/themes/starfield -CFLAGS_GNULIB = -Wno-undef -Wno-sign-compare -Wno-unused -Wno-unused-parameter -Wno-redundant-decls -Wno-unreachable-code -Wno-conversion +CFLAGS_GNULIB = -Wno-undef -Wno-sign-compare -Wno-unused -Wno-unused-parameter -Wno-redundant-decls -Wno-unreachable-code -Wno-conversion -Wno-error=attributes CPPFLAGS_GNULIB = -I$(top_builddir)/grub-core/lib/gnulib -I$(top_srcdir)/grub-core/lib/gnulib CFLAGS_POSIX = -fno-builtin From 3d4cb5a4328768b198d86e1b6efb18f30f06ba0e Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Thu, 24 Aug 2023 23:24:30 +0200 Subject: [PATCH 098/402] build: Rename HAVE_LIBZFS to USE_LIBZFS The HAVE_LIBZFS is defined by libzfs test and hence conflicts with manual definition. On NetBSD it ends up detecting zfs but not detecting nvpair and creates confusion. Split them. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- configure.ac | 6 ++---- grub-core/osdep/unix/getroot.c | 4 ++-- util/getroot.c | 6 +++--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/configure.ac b/configure.ac index c19779c14..5e6e7d377 100644 --- a/configure.ac +++ b/configure.ac @@ -2008,11 +2008,9 @@ fi if test x"$libzfs_excuse" = x ; then # We need both libzfs and libnvpair for a successful build. LIBZFS="-lzfs" - AC_DEFINE([HAVE_LIBZFS], [1], - [Define to 1 if you have the ZFS library.]) LIBNVPAIR="-lnvpair" - AC_DEFINE([HAVE_LIBNVPAIR], [1], - [Define to 1 if you have the NVPAIR library.]) + AC_DEFINE([USE_LIBZFS], [1], + [Define to 1 if ZFS library should be used.]) fi AC_SUBST([LIBZFS]) diff --git a/grub-core/osdep/unix/getroot.c b/grub-core/osdep/unix/getroot.c index cde821eb9..71cdf2e86 100644 --- a/grub-core/osdep/unix/getroot.c +++ b/grub-core/osdep/unix/getroot.c @@ -53,7 +53,7 @@ #include -#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR) +#ifdef USE_LIBZFS # include # include #endif @@ -158,7 +158,7 @@ grub_util_find_root_devices_from_poolname (char *poolname) size_t ndevices = 0; size_t devices_allocated = 0; -#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR) +#ifdef USE_LIBZFS zpool_handle_t *zpool; libzfs_handle_t *libzfs; nvlist_t *config, *vdev_tree; diff --git a/util/getroot.c b/util/getroot.c index 086581998..75a7d5f21 100644 --- a/util/getroot.c +++ b/util/getroot.c @@ -47,7 +47,7 @@ #include -#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR) +#ifdef USE_LIBZFS # include # include #endif @@ -456,7 +456,7 @@ grub_util_biosdisk_is_present (const char *os_dev) return ret; } -#ifdef HAVE_LIBZFS +#ifdef USE_LIBZFS static libzfs_handle_t *__libzfs_handle; static void @@ -478,5 +478,5 @@ grub_get_libzfs_handle (void) return __libzfs_handle; } -#endif /* HAVE_LIBZFS */ +#endif /* USE_LIBZFS */ From 2d6a89980618003253684109c00f239e411d83fe Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Thu, 24 Aug 2023 23:25:48 +0200 Subject: [PATCH 099/402] autogen: Accept python3.10 as a python alternative NetBSD doesn't provide python or python3. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- autogen.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autogen.sh b/autogen.sh index 5a5c356fd..195daa541 100755 --- a/autogen.sh +++ b/autogen.sh @@ -9,7 +9,7 @@ fi # Detect python if [ -z "$PYTHON" ]; then - for i in python3 python; do + for i in python3 python3.10 python; do if command -v "$i" > /dev/null 2>&1; then PYTHON="$i" echo "Using $PYTHON..." From 52dbf66ea443827358b97d3383abc8b1b8751474 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Fri, 25 Aug 2023 00:00:52 +0200 Subject: [PATCH 100/402] configure: Add *BSD font paths *BSD puts fonts in other places. Add them to the list. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 5e6e7d377..1fffe996d 100644 --- a/configure.ac +++ b/configure.ac @@ -1817,7 +1817,7 @@ if test "x$with_dejavufont" = x; then # search in well-known directories if test x"$starfield_excuse" = x; then for ext in pcf pcf.gz bdf bdf.gz ttf ttf.gz; do - for dir in . /usr/src /usr/share/fonts/X11/misc /usr/share/fonts/truetype/ttf-dejavu /usr/share/fonts/dejavu /usr/share/fonts/truetype; do + for dir in . /usr/src /usr/share/fonts/X11/misc /usr/share/fonts/truetype/ttf-dejavu /usr/share/fonts/dejavu /usr/share/fonts/truetype /usr/pkg/share/fonts/X11/TTF /usr/local/share/fonts/dejavu /usr/X11R6/lib/X11/fonts/TTF; do if test -f "$dir/DejaVuSans.$ext"; then DJVU_FONT_SOURCE="$dir/DejaVuSans.$ext" break 2 @@ -1846,7 +1846,7 @@ AC_ARG_WITH([unifont], if test "x$with_unifont" = x; then # search in well-known directories for ext in pcf pcf.gz bdf bdf.gz ttf ttf.gz; do - for dir in . /usr/src /usr/share/fonts/X11/misc /usr/share/fonts/unifont /usr/share/fonts/uni /usr/share/fonts/truetype/unifont /usr/share/fonts/misc; do + for dir in . /usr/src /usr/share/fonts/X11/misc /usr/share/fonts/unifont /usr/share/fonts/uni /usr/share/fonts/truetype/unifont /usr/share/fonts/misc /usr/pkg/share/fonts/X11/misc /usr/local/share/fonts/gnu-unifont /usr/local/share/fonts/unifont; do if test -f "$dir/unifont.$ext"; then md5="$(md5sum "$dir/unifont.$ext"|awk '{ print $1; }')" # PCF and BDF from version 6.3 isn't hanled properly by libfreetype. From b3d49a697b1b177d59f50bc48f6ca269d46f5d92 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Fri, 25 Aug 2023 01:54:09 +0200 Subject: [PATCH 101/402] configure: Add RPATH for freetype on NetBSD Without this build-time mkfont fails dynamic linking. This is not ideal but improves the situation until a better solution is available. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- configure.ac | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/configure.ac b/configure.ac index 1fffe996d..8e16ce25f 100644 --- a/configure.ac +++ b/configure.ac @@ -1716,6 +1716,9 @@ if test x"$grub_mkfont_excuse" = x ; then CPPFLAGS="$SAVED_CPPFLAGS" LIBS="$SAVED_LIBS" ], [grub_mkfont_excuse=["need freetype2 library"]]) + if test x"$grub_mkfont_excuse" = x && test x"$host_kernel" = xnetbsd ; then + FREETYPE_LIBS="$FREETYPE_LIBS -Wl,-R,/usr/pkg/lib" ; + fi fi if test x"$enable_grub_mkfont" = xyes && test x"$grub_mkfont_excuse" != x ; then @@ -1770,6 +1773,11 @@ if test x"$grub_build_mkfont_excuse" = x ; then LIBS="$SAVED_LIBS" CPPFLAGS="$SAVED_CPPFLAGS_2" ], [grub_build_mkfont_excuse=["need freetype2 library"]]) + if test x"$grub_build_mkfont_excuse" = x ; then + case x"$build_os" in + xnetbsd*) BUILD_FREETYPE_LIBS="$BUILD_FREETYPE_LIBS -Wl,-R,/usr/pkg/lib" ;; + esac + fi PKG_CONFIG="$SAVED_PKG_CONFIG" fi From 6d2aa7ee0158ad1218fe8ec4ba96fe04ff21486b Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Fri, 25 Aug 2023 13:37:43 +0200 Subject: [PATCH 102/402] kern/mm: Use %x and cast for displaying sizeof() There is some variance in how compiler treats sizeof() especially on 32-bit platforms where it can be naturally either int or long. Explicit cast solves the issue. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/kern/mm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c index 792ab0a83..027a25cd1 100644 --- a/grub-core/kern/mm.c +++ b/grub-core/kern/mm.c @@ -238,8 +238,8 @@ grub_mm_init_region (void *addr, grub_size_t size) * ||-q->post_size-|----size-----| */ grub_dprintf ("regions", "Can we extend into region below?" - " %p + %" PRIxGRUB_SIZE " + %" PRIxGRUB_SIZE " + %" PRIxGRUB_SIZE " ?=? %p\n", - (grub_uint8_t *) q, sizeof(*q), q->size, q->post_size, (grub_uint8_t *) addr); + " %p + %x + %" PRIxGRUB_SIZE " + %" PRIxGRUB_SIZE " ?=? %p\n", + (grub_uint8_t *) q, (int) sizeof (*q), q->size, q->post_size, (grub_uint8_t *) addr); if ((grub_uint8_t *) q + sizeof (*q) + q->size + q->post_size == (grub_uint8_t *) addr) { From a13df3d15a2c803006d7dcad4e89de1a8d71a2ea Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Thu, 24 Aug 2023 21:47:50 +0200 Subject: [PATCH 103/402] bootstrap: Don't check gettext version NetBSD gettext is older than the check but we don't actually need 0.18.3, older one works fine. This is needed to make bootstrap work on NetBSD. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- INSTALL | 2 +- bootstrap.conf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/INSTALL b/INSTALL index b93fe9c61..8d9207c84 100644 --- a/INSTALL +++ b/INSTALL @@ -20,7 +20,7 @@ configuring the GRUB. for i386, x86_64, arm (including thumb), arm64, mips(el), powerpc, sparc64 * GNU Make * GNU Bison 2.3 or later -* GNU gettext 0.17 or later +* GNU gettext * GNU binutils 2.9.1.0.23 or later * Flex 2.5.35 or later * pkg-config diff --git a/bootstrap.conf b/bootstrap.conf index 64ea9bcb5..7a7813d28 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -68,7 +68,7 @@ SKIP_PO=t buildreq="\ autoconf 2.64 automake 1.14 -gettext 0.18.3 +gettext - git 1.5.5 patch - tar - From f20123072a52c31b2ab9295266a6bb341f77e1fa Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Fri, 25 Aug 2023 00:01:25 +0200 Subject: [PATCH 104/402] libnvpair: Support prefixed nvlist symbol names as found on NetBSD NetBSD uses slightly different function names for the same functions. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- configure.ac | 15 +++++++++++++-- grub-core/osdep/unix/getroot.c | 8 ++++---- include/grub/util/libnvpair.h | 12 +++++++++--- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/configure.ac b/configure.ac index 8e16ce25f..c1b98b13d 100644 --- a/configure.ac +++ b/configure.ac @@ -2005,8 +2005,19 @@ fi if test x"$libzfs_excuse" = x ; then AC_CHECK_LIB([nvpair], [nvlist_lookup_string], - [], - [libzfs_excuse="need nvpair library"]) + [have_normal_nvpair=yes], + [have_normal_nvpair=no]) + if test x"$have_normal_nvpair" = xno ; then + AC_CHECK_LIB([nvpair], [opensolaris_nvlist_lookup_string], + [have_prefixed_nvpair=yes], + [have_prefixed_nvpair=no]) + if test x"$have_prefixed_nvpair" = xyes ; then + AC_DEFINE([GRUB_UTIL_NVPAIR_IS_PREFIXED], [1], + [Define to 1 if libnvpair symbols are prefixed with opensolaris_.]) + else + libzfs_excuse="need nvpair library" + fi + fi fi if test x"$enable_libzfs" = xyes && test x"$libzfs_excuse" != x ; then diff --git a/grub-core/osdep/unix/getroot.c b/grub-core/osdep/unix/getroot.c index 71cdf2e86..ee11b02fb 100644 --- a/grub-core/osdep/unix/getroot.c +++ b/grub-core/osdep/unix/getroot.c @@ -174,20 +174,20 @@ grub_util_find_root_devices_from_poolname (char *poolname) zpool = zpool_open (libzfs, poolname); config = zpool_get_config (zpool, NULL); - if (nvlist_lookup_nvlist (config, "vdev_tree", &vdev_tree) != 0) + if (NVLIST(lookup_nvlist) (config, "vdev_tree", &vdev_tree) != 0) error (1, errno, "nvlist_lookup_nvlist (\"vdev_tree\")"); - if (nvlist_lookup_nvlist_array (vdev_tree, "children", &children, &nvlist_count) != 0) + if (NVLIST(lookup_nvlist_array) (vdev_tree, "children", &children, &nvlist_count) != 0) error (1, errno, "nvlist_lookup_nvlist_array (\"children\")"); assert (nvlist_count > 0); - while (nvlist_lookup_nvlist_array (children[0], "children", + while (NVLIST(lookup_nvlist_array) (children[0], "children", &children, &nvlist_count) == 0) assert (nvlist_count > 0); for (i = 0; i < nvlist_count; i++) { - if (nvlist_lookup_string (children[i], "path", &device) != 0) + if (NVLIST(lookup_string) (children[i], "path", &device) != 0) error (1, errno, "nvlist_lookup_string (\"path\")"); struct stat st; diff --git a/include/grub/util/libnvpair.h b/include/grub/util/libnvpair.h index 573c7ea81..a3acffb88 100644 --- a/include/grub/util/libnvpair.h +++ b/include/grub/util/libnvpair.h @@ -29,9 +29,15 @@ typedef void nvlist_t; -int nvlist_lookup_string (nvlist_t *, const char *, char **); -int nvlist_lookup_nvlist (nvlist_t *, const char *, nvlist_t **); -int nvlist_lookup_nvlist_array (nvlist_t *, const char *, nvlist_t ***, unsigned int *); +#ifdef GRUB_UTIL_NVPAIR_IS_PREFIXED +#define NVLIST(x) opensolaris_nvlist_ ## x +#else +#define NVLIST(x) nvlist_ ## x +#endif + +int NVLIST(lookup_string) (nvlist_t *, const char *, char **); +int NVLIST(lookup_nvlist) (nvlist_t *, const char *, nvlist_t **); +int NVLIST(lookup_nvlist_array) (nvlist_t *, const char *, nvlist_t ***, unsigned int *); #endif /* ! HAVE_LIBNVPAIR_H */ From 63fc253fc9f148c09d5bb38971edcb50dc090f9d Mon Sep 17 00:00:00 2001 From: Qiumiao Zhang Date: Mon, 11 Dec 2023 17:20:25 +0800 Subject: [PATCH 105/402] commands/acpi: Fix calculation of ACPI tables addresses when processing RSDT and XSDT According to the ACPI specification the XSDT Entry field contains an array of 64-bit physical addresses which points to other DESCRIPTION_HEADERs. However, the entry_ptr iterator is defined as a 32-bit pointer. It means each 64-bit entry in the XSDT table is treated as two separate 32-bit entries then. Fix the issue by using correct addresses sizes when processing RSDT and XSDT tables. Signed-off-by: Qiumiao Zhang Reviewed-by: Daniel Kiper --- grub-core/commands/acpi.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/grub-core/commands/acpi.c b/grub-core/commands/acpi.c index 1c034463c..77be99a58 100644 --- a/grub-core/commands/acpi.c +++ b/grub-core/commands/acpi.c @@ -490,12 +490,12 @@ grub_cmd_acpi (struct grub_extcmd_context *ctxt, int argc, char **args) if (rsdp) { - grub_uint32_t *entry_ptr; + grub_uint8_t *entry_ptr; char *exclude = 0; char *load_only = 0; char *ptr; - /* RSDT consists of header and an array of 32-bit pointers. */ - struct grub_acpi_table_header *rsdt; + grub_size_t tbl_addr_size; + struct grub_acpi_table_header *table_head; exclude = state[0].set ? grub_strdup (state[0].arg) : 0; if (exclude) @@ -515,20 +515,31 @@ grub_cmd_acpi (struct grub_extcmd_context *ctxt, int argc, char **args) rev1 = ! rsdp->revision; rev2 = rsdp->revision; if (rev2 && ((struct grub_acpi_table_header *) (grub_addr_t) ((struct grub_acpi_rsdp_v20 *) rsdp)->xsdt_addr) != NULL) - rsdt = (struct grub_acpi_table_header *) (grub_addr_t) ((struct grub_acpi_rsdp_v20 *) rsdp)->xsdt_addr; + { + /* XSDT consists of header and an array of 64-bit pointers. */ + table_head = (struct grub_acpi_table_header *) (grub_addr_t) ((struct grub_acpi_rsdp_v20 *) rsdp)->xsdt_addr; + tbl_addr_size = sizeof (((struct grub_acpi_rsdp_v20 *) rsdp)->xsdt_addr); + } else - rsdt = (struct grub_acpi_table_header *) (grub_addr_t) rsdp->rsdt_addr; + { + /* RSDT consists of header and an array of 32-bit pointers. */ + table_head = (struct grub_acpi_table_header *) (grub_addr_t) rsdp->rsdt_addr; + tbl_addr_size = sizeof (rsdp->rsdt_addr); + } /* Load host tables. */ - for (entry_ptr = (grub_uint32_t *) (rsdt + 1); - entry_ptr < (grub_uint32_t *) (((grub_uint8_t *) rsdt) - + rsdt->length); - entry_ptr++) + for (entry_ptr = (grub_uint8_t *) (table_head + 1); + entry_ptr < (grub_uint8_t *) (((grub_uint8_t *) table_head) + table_head->length); + entry_ptr += tbl_addr_size) { char signature[5]; struct efiemu_acpi_table *table; - struct grub_acpi_table_header *curtable - = (struct grub_acpi_table_header *) (grub_addr_t) *entry_ptr; + struct grub_acpi_table_header *curtable; + if (tbl_addr_size == sizeof (rsdp->rsdt_addr)) + curtable = (struct grub_acpi_table_header *) (grub_addr_t) *((grub_uint32_t *) entry_ptr); + else + curtable = (struct grub_acpi_table_header *) (grub_addr_t) *((grub_uint64_t *) entry_ptr); + signature[4] = 0; for (i = 0; i < 4;i++) signature[i] = grub_tolower (curtable->signature[i]); From 7c8ae7dcbd59a963130a7aaae7a7348334465f74 Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Wed, 13 Dec 2023 22:25:13 +0000 Subject: [PATCH 106/402] gfxmenu/gui_image: Fix double free of bitmap In grub-core/gfxmenu/gui_image.c, Coverity detected a double free in the function load_image(). The function checks if self->bitmap and self->raw_bitmap aren't NULL and then frees them. In the case self->bitmap and self->raw_bitmap are the same, only self->raw_bitmap is freed which would also free the memory used by self->bitmap. However, in this case self->bitmap isn't being set to NULL which could lead to a double free later in the code. After self->raw_bitmap is freed, it gets set to the variable bitmap. If this variable is NULL, the code could have a path that would free self->bitmap a second time in the function rescale_image(). Fixes: CID 292472 Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- grub-core/gfxmenu/gui_image.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/grub-core/gfxmenu/gui_image.c b/grub-core/gfxmenu/gui_image.c index 6b2e976f1..eed4b6b87 100644 --- a/grub-core/gfxmenu/gui_image.c +++ b/grub-core/gfxmenu/gui_image.c @@ -195,13 +195,16 @@ load_image (grub_gui_image_t self, const char *path) return grub_errno; if (self->bitmap && (self->bitmap != self->raw_bitmap)) - { - grub_video_bitmap_destroy (self->bitmap); - self->bitmap = 0; - } + grub_video_bitmap_destroy (self->bitmap); if (self->raw_bitmap) grub_video_bitmap_destroy (self->raw_bitmap); + /* + * Either self->bitmap is being freed or it shares memory with + * self->raw_bitmap which is being freed. To ensure self->bitmap doesn't + * point to memory that has been freed, we can set it to NULL. + */ + self->bitmap = NULL; self->raw_bitmap = bitmap; return rescale_image (self); } From e424e945c9298409e1c9b585a234d6676f3d4432 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 18 Dec 2023 23:59:59 -0600 Subject: [PATCH 107/402] efi: Initialize canary to non-zero value The canary, __stack_chk_guard, is in the BSS and so will get initialized to zero if it is not explicitly initialized. If the UEFI firmware does not support the RNG protocol, then the canary will not be randomized and will be zero. This seems like a possibly easier value to write by an attacker. Initialize canary to static random bytes, so that it is still random when there is no RNG protocol. Set at least one byte to NUL to protect against string buffer overflow attacks [1]. Code that writes NUL terminated strings will terminate when a NUL is encountered in the input byte stream. So the attacker will not be able to forge the canary by including it in the input stream without terminating the string operation and thus limiting the stack corruption. [1] https://www.sans.org/blog/stack-canaries-gingerly-sidestepping-the-cage/ Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- grub-core/kern/efi/init.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c index e759cc315..08e24d46f 100644 --- a/grub-core/kern/efi/init.c +++ b/grub-core/kern/efi/init.c @@ -45,7 +45,8 @@ static grub_guid_t rng_protocol_guid = GRUB_EFI_RNG_PROTOCOL_GUID; */ static grub_efi_uint8_t stack_chk_guard_buf[32]; -grub_addr_t __stack_chk_guard; +/* Initialize canary in case there is no RNG protocol. */ +grub_addr_t __stack_chk_guard = (grub_addr_t) 0x00f2b7e2f193b25c; void __attribute__ ((noreturn)) __stack_chk_fail (void) From dcc1af5d688e32ec64e0125ac387c27fc7a7c6d1 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Tue, 19 Dec 2023 00:00:00 -0600 Subject: [PATCH 108/402] efi: Generate stack protector canary at build time if urandom is available Generating the canary at build time allows the canary to be different for every build which could limit the effectiveness of certain exploits. Fallback to the statically generated random bytes if /dev/urandom is not readable, e.g. Windows. On 32-bit architectures, which use a 32-bit canary, reduce the canary to 4 bytes with one byte being NUL to filter out string buffer overflow attacks. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- config.h.in | 2 ++ configure.ac | 20 ++++++++++++++++++++ grub-core/kern/efi/init.c | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/config.h.in b/config.h.in index 4d1e50eba..9b1d39971 100644 --- a/config.h.in +++ b/config.h.in @@ -64,6 +64,8 @@ # define GRUB_TARGET_CPU "@GRUB_TARGET_CPU@" # define GRUB_PLATFORM "@GRUB_PLATFORM@" +# define GRUB_STACK_PROTECTOR_INIT @GRUB_STACK_PROTECTOR_INIT@ + # define RE_ENABLE_I18N 1 # define _GNU_SOURCE 1 diff --git a/configure.ac b/configure.ac index c1b98b13d..b8c5c78d5 100644 --- a/configure.ac +++ b/configure.ac @@ -1438,6 +1438,26 @@ else AC_MSG_ERROR([invalid value $enable_stack_protector for --enable-stack-protector]) fi TARGET_CPPFLAGS="$TARGET_CPPFLAGS -DGRUB_STACK_PROTECTOR=1" + + if test -r /dev/urandom; then + # Generate the 8 byte stack protector canary at build time if /dev/urandom + # is able to be read. The first byte should be NUL to filter out string + # buffer overflow attacks. + GRUB_STACK_PROTECTOR_INIT="$($PYTHON -c 'import codecs; rf=open("/dev/urandom", "rb"); print("0x00"+codecs.encode(rf.read(7), "hex").decode("ascii"))')" + else + # Some hosts may not have a urandom, e.g. Windows, so use statically + # generated random bytes + GRUB_STACK_PROTECTOR_INIT="0x00f2b7e2f193b25c" + fi + + if test x"$target_m32" = x1 ; then + # Make sure that the canary default value is 24-bits by only using the + # lower 3 bytes on 32 bit systems. This allows the upper byte to be NUL + # to filter out string buffer overflow attacks. + GRUB_STACK_PROTECTOR_INIT="0x00$(echo "$GRUB_STACK_PROTECTOR_INIT" | sed 's/.*\(......\)$/\1/')" + fi + + AC_SUBST([GRUB_STACK_PROTECTOR_INIT]) fi CFLAGS="$TARGET_CFLAGS" diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c index 08e24d46f..6c54af6e7 100644 --- a/grub-core/kern/efi/init.c +++ b/grub-core/kern/efi/init.c @@ -46,7 +46,7 @@ static grub_guid_t rng_protocol_guid = GRUB_EFI_RNG_PROTOCOL_GUID; static grub_efi_uint8_t stack_chk_guard_buf[32]; /* Initialize canary in case there is no RNG protocol. */ -grub_addr_t __stack_chk_guard = (grub_addr_t) 0x00f2b7e2f193b25c; +grub_addr_t __stack_chk_guard = (grub_addr_t) GRUB_STACK_PROTECTOR_INIT; void __attribute__ ((noreturn)) __stack_chk_fail (void) From 477a0dbd5e6311e94cecb30a2f95dabb8205b8df Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Tue, 19 Dec 2023 00:00:01 -0600 Subject: [PATCH 109/402] efi: Add support for reproducible builds Having randomly generated bytes in the binary output breaks reproducible builds. Since build timestamps are usually the source of irreproducibility there is a standard which defines an environment variable SOURCE_DATE_EPOCH to be used when set for build timestamps. According to the standard [1], the value of SOURCE_DATE_EPOCH is a base-10 integer of the number of seconds since the UNIX epoch. Currently, this is a 10 digit number that fits into 32-bits, but will not shortly after the year 2100. So to be future-proof only use the least significant 32-bits. On 64-bit architectures, where the canary is also 64-bits, there is an extra 32-bits that can be filled to provide more entropy. The first byte is NUL to filter out string buffer overflow attacks and the remaining 24-bits are set to static random bytes. [1] https://reproducible-builds.org/specs/source-date-epoch Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- configure.ac | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index b8c5c78d5..cb7abc573 100644 --- a/configure.ac +++ b/configure.ac @@ -1439,7 +1439,9 @@ else fi TARGET_CPPFLAGS="$TARGET_CPPFLAGS -DGRUB_STACK_PROTECTOR=1" - if test -r /dev/urandom; then + if test -n "$SOURCE_DATE_EPOCH"; then + GRUB_STACK_PROTECTOR_INIT="0x00f2b7e2$(printf "%x" "$SOURCE_DATE_EPOCH" | sed 's/.*\(........\)$/\1/')" + elif test -r /dev/urandom; then # Generate the 8 byte stack protector canary at build time if /dev/urandom # is able to be read. The first byte should be NUL to filter out string # buffer overflow attacks. From 5ca9db22e8ed0dbebb2aec53722972de0680a463 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Wed, 20 Dec 2023 16:54:46 +0100 Subject: [PATCH 110/402] Release 2.12 Signed-off-by: Daniel Kiper --- NEWS | 20 ++++++++++++++++++++ configure.ac | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 73b8492bc..310130962 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,23 @@ +New in 2.12: + +* GCC 13 support. +* clang 14 support. +* binutils 2.38 support. +* Unification of EFI Linux kernel loader across architectures. +* Transition to EFI Linux kernel stub loader for x86 architecture. +* Initial support for Boot Loader Interface. +* Support for dynamic GRUB runtime memory addition using firmware calls. +* PCI and MMIO UARTs support. +* SDL2 support. +* LoongArch support. +* TPM driver fixes. +* Many filesystems fixes. +* Many CVE and Coverity fixes. +* Debugging support improvements. +* Tests improvements. +* Documentation improvements. +* ...and tons of other fixes and cleanups... + New in 2.06: * GCC 10 support. diff --git a/configure.ac b/configure.ac index cb7abc573..cd667a2eb 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.12~rc1],[bug-grub@gnu.org]) +AC_INIT([GRUB],[2.12],[bug-grub@gnu.org]) AS_CASE(["$ERROR_PLATFORM_NOT_SUPPORT_SSP"], [n | no | nO | N | No | NO], [ERROR_PLATFORM_NOT_SUPPORT_SSP=no], From 8961305b4ee686fb5abf1a7c0612794f28056593 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Wed, 20 Dec 2023 17:25:46 +0100 Subject: [PATCH 111/402] Bump version to 2.13 Signed-off-by: Daniel Kiper --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index cd667a2eb..84a202c6e 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.12],[bug-grub@gnu.org]) +AC_INIT([GRUB],[2.13],[bug-grub@gnu.org]) AS_CASE(["$ERROR_PLATFORM_NOT_SUPPORT_SSP"], [n | no | nO | N | No | NO], [ERROR_PLATFORM_NOT_SUPPORT_SSP=no], From b835601c7639ed1890f2d3db91900a8506011a8e Mon Sep 17 00:00:00 2001 From: Oskari Pirhonen Date: Thu, 21 Dec 2023 01:17:35 -0600 Subject: [PATCH 112/402] build: Include grub-core/extra_deps.lst in dist Fixes build failure due to the extra_deps.lst file not existing in the tarball. Found while trying to package GRUB 2.12 for Gentoo. make[3]: *** No rule to make target '/var/tmp/portage/sys-boot/grub-2.12/work/grub-2.12/grub-core/extra_deps.lst', needed by 'syminfo.lst'. Stop. Fixes: 89fbe0cac (grub-core/Makefile.am: Make path to extra_deps.lst relative to $(top_srcdir)/grub-core) Fixes: 154dcb1ae (build: Allow explicit module dependencies) Signed-off-by: Oskari Pirhonen 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 5e7126f98..d9e2b8cc7 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -21,6 +21,7 @@ EXTRA_DIST += conf/i386-cygwin-img-ld.sc EXTRA_DIST += grub-core/Makefile.core.def EXTRA_DIST += grub-core/Makefile.gcry.def +EXTRA_DIST += grub-core/extra_deps.lst EXTRA_DIST += grub-core/genmoddep.awk EXTRA_DIST += grub-core/genmod.sh.in EXTRA_DIST += grub-core/gensyminfo.sh.in From 5a311d029fb3b0526551ea89b8f16842355f0d03 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 29 Dec 2023 17:08:20 +0800 Subject: [PATCH 113/402] tests: Switch password quality check off for luks2 test When adding/changing the password for the luks2 partition, cryptsetup may reject the command due to the weak password. Since this is only for testing, add "--force-password" to switch password quality check off to avoid the unexpected failure. Signed-off-by: Gary Lin Reviewed-by: Glenn Washburn Reviewed-by: Daniel Kiper --- tests/grub_cmd_cryptomount.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/grub_cmd_cryptomount.in b/tests/grub_cmd_cryptomount.in index 4578ab709..f4d8f3547 100644 --- a/tests/grub_cmd_cryptomount.in +++ b/tests/grub_cmd_cryptomount.in @@ -173,7 +173,7 @@ eval testcase_fail "'LUKS2 test with argon2 pbkdf:'" \ # Add good password to second slot and change first slot to unchecked password csscript=`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` || exit 99 cat >$csscript <<'EOF' - CSOPTS="--pbkdf-force-iterations 1000 --pbkdf=pbkdf2" + CSOPTS="--pbkdf-force-iterations 1000 --pbkdf=pbkdf2 --force-password" cryptsetup $CSOPTS --key-file $lukskeyfile luksAddKey $luksdiskfile $lukskeyfile echo "newpass" | cryptsetup $CSOPTS --key-file $lukskeyfile --key-slot 0 luksChangeKey $luksdiskfile EOF From c8bf75875761592e4019c6968725309cb1e79a13 Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Sat, 20 Jan 2024 02:52:49 +0000 Subject: [PATCH 114/402] fs/jfs: Clean up redundant code In grub-core/fs/jfs.c, Coverity spotted redundant code where the pointer diro was being set to 0 and then being overwritten later without being used. Since this is unnecessary, we can remove the code that sets diro to 0. Fixes: CID 428876 Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- grub-core/fs/jfs.c | 1 - 1 file changed, 1 deletion(-) diff --git a/grub-core/fs/jfs.c b/grub-core/fs/jfs.c index 6f7c43904..62e20ef6f 100644 --- a/grub-core/fs/jfs.c +++ b/grub-core/fs/jfs.c @@ -716,7 +716,6 @@ grub_jfs_find_file (struct grub_jfs_data *data, const char *path, grub_uint32_t dirino = grub_le_to_cpu32 (data->currinode.inode); grub_jfs_closedir (diro); - diro = 0; if (grub_jfs_read_inode (data, ino, &data->currinode)) break; From db0d19dc5f85a6404f7e6a2d52bbeec0ea20579d Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Sat, 20 Jan 2024 02:52:50 +0000 Subject: [PATCH 115/402] osdep/unix/getroot: Clean up redundant code In grub-core/osdep/unix/getroot.c, Coverity spotted redundant code where the double pointer os_dev was being set to 0 and then being overwritten later without being used. Since this is unnecessary, we can remove the code that sets os_dev to 0. Fixes: CID 428875 Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- grub-core/osdep/unix/getroot.c | 1 - 1 file changed, 1 deletion(-) diff --git a/grub-core/osdep/unix/getroot.c b/grub-core/osdep/unix/getroot.c index ee11b02fb..c7aa202ab 100644 --- a/grub-core/osdep/unix/getroot.c +++ b/grub-core/osdep/unix/getroot.c @@ -540,7 +540,6 @@ grub_guess_root_devices (const char *dir_in) for (cur = os_dev; *cur; cur++) free (*cur); free (os_dev); - os_dev = 0; } if (stat (dir, &st) < 0) From d89a2a6e57593f78e8675f1765032a0d2afad62f Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Sat, 20 Jan 2024 02:52:51 +0000 Subject: [PATCH 116/402] loader/i386/multiboot_mbi: Clean up redundant code In grub-core/loader/i386/multiboot_mbi.c, Coverity spotted redundant code where the variable err was being set to GRUB_ERR_NONE and then being overwritten later without being used. Since this is unnecessary, we can remove the code that sets err to GRUB_ERR_NONE. Fixes: CID 428877 Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- grub-core/loader/i386/multiboot_mbi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/loader/i386/multiboot_mbi.c b/grub-core/loader/i386/multiboot_mbi.c index 11a6e224f..fae5d6fb8 100644 --- a/grub-core/loader/i386/multiboot_mbi.c +++ b/grub-core/loader/i386/multiboot_mbi.c @@ -86,7 +86,7 @@ load_kernel (grub_file_t file, const char *filename, return GRUB_ERR_NONE; } if (err == GRUB_ERR_UNKNOWN_OS && (header->flags & MULTIBOOT_AOUT_KLUDGE)) - grub_errno = err = GRUB_ERR_NONE; + grub_errno = GRUB_ERR_NONE; } if (header->flags & MULTIBOOT_AOUT_KLUDGE) { From a8c050451581bc732998743d097ace9ca07620a2 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Tue, 23 Jan 2024 21:47:36 +0100 Subject: [PATCH 117/402] osdep/hurd/getroot: Fix 64-bit build The file_get_fs_options() takes a mach_msg_type_number_t, 32-bit, not a size_t, 64-bit on 64-bit platforms. Signed-off-by: Samuel Thibault Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/osdep/hurd/getroot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/osdep/hurd/getroot.c b/grub-core/osdep/hurd/getroot.c index 0efefdab4..b849700e6 100644 --- a/grub-core/osdep/hurd/getroot.c +++ b/grub-core/osdep/hurd/getroot.c @@ -58,7 +58,7 @@ grub_util_find_hurd_root_device (const char *path) file_t file; error_t err; char *argz = NULL, *name = NULL, *ret; - size_t argz_len = 0; + mach_msg_type_number_t argz_len = 0; int i; file = file_name_lookup (path, 0, 0); From 9e1b18fc17b623446c1adf0466158c7cf7a58303 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Tue, 23 Jan 2024 21:47:56 +0100 Subject: [PATCH 118/402] util/grub.d/10_hurd.in: Find proper ld.so on 64-bit systems The 64-bit ABI defines ld.so to be /lib/ld-x86-64.so.1. Signed-off-by: Samuel Thibault Reviewed-by: Daniel Kiper --- util/grub.d/10_hurd.in | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/util/grub.d/10_hurd.in b/util/grub.d/10_hurd.in index 7d1e46391..f9c394ecb 100644 --- a/util/grub.d/10_hurd.in +++ b/util/grub.d/10_hurd.in @@ -91,7 +91,14 @@ if ${at_least_one} ; then : ; else exit 0 fi -if ${all_of_them} && test -e /lib/ld.so.1 ; then : ; else +if test -e '/lib/ld.so.1' ; then + LD_SO=/lib/ld.so.1 +fi +if test -e '/lib/ld-x86-64.so.1' ; then + LD_SO=/lib/ld-x86-64.so.1 +fi + +if ${all_of_them} && test -n "$LD_SO" ; then : ; else gettext "Some Hurd stuff found, but not enough to boot." >&2 echo >&2 exit 1 @@ -210,7 +217,7 @@ EOF $host_ports \\ --exec-server-task='\${exec-task}' -T typed '\${root}' \\ '\$(fs-task=task-create)' $resume_task - module /lib/ld.so.1 exec /hurd/exec '\$(exec-task=task-create)' + module $LD_SO exec /hurd/exec '\$(exec-task=task-create)' } EOF From 4380c2d8ad72e4f7ffd3dae3ad1de78159c2f2ba Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Tue, 30 Jan 2024 14:41:10 +0800 Subject: [PATCH 119/402] util/bash-completion: Load scripts on demand There are two system directories for bash-completion scripts. One is /usr/share/bash-completion/completions/ and the other is /etc/bash_completion.d/. The "etc" scripts are loaded in advance and for backward compatibility while the "usr" scripts are loaded on demand. To load scripts on demand it requires a corresponding script for every command. So, the main bash-completion script is split into several subscripts for different "grub-*" commands. To share the code the real completion functions are still implemented in "grub" and each subscript sources "grub" and invokes the corresponding function. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- util/bash-completion.d/Makefile.am | 114 +++++++++++++++++- .../bash-completion.d/grub-bios-setup.bash.in | 30 +++++ .../bash-completion.d/grub-completion.bash.in | 89 ++------------ util/bash-completion.d/grub-editenv.bash.in | 30 +++++ util/bash-completion.d/grub-install.bash.in | 30 +++++ util/bash-completion.d/grub-mkconfig.bash.in | 30 +++++ util/bash-completion.d/grub-mkfont.bash.in | 30 +++++ util/bash-completion.d/grub-mkimage.bash.in | 30 +++++ .../grub-mkpasswd-pbkdf2.bash.in | 30 +++++ util/bash-completion.d/grub-mkrescue.bash.in | 30 +++++ util/bash-completion.d/grub-probe.bash.in | 30 +++++ util/bash-completion.d/grub-reboot.bash.in | 30 +++++ .../grub-script-check.bash.in | 30 +++++ .../grub-set-default.bash.in | 30 +++++ .../grub-sparc64-setup.bash.in | 30 +++++ 15 files changed, 510 insertions(+), 83 deletions(-) create mode 100644 util/bash-completion.d/grub-bios-setup.bash.in create mode 100644 util/bash-completion.d/grub-editenv.bash.in create mode 100644 util/bash-completion.d/grub-install.bash.in create mode 100644 util/bash-completion.d/grub-mkconfig.bash.in create mode 100644 util/bash-completion.d/grub-mkfont.bash.in create mode 100644 util/bash-completion.d/grub-mkimage.bash.in create mode 100644 util/bash-completion.d/grub-mkpasswd-pbkdf2.bash.in create mode 100644 util/bash-completion.d/grub-mkrescue.bash.in create mode 100644 util/bash-completion.d/grub-probe.bash.in create mode 100644 util/bash-completion.d/grub-reboot.bash.in create mode 100644 util/bash-completion.d/grub-script-check.bash.in create mode 100644 util/bash-completion.d/grub-set-default.bash.in create mode 100644 util/bash-completion.d/grub-sparc64-setup.bash.in diff --git a/util/bash-completion.d/Makefile.am b/util/bash-completion.d/Makefile.am index 136287cf1..33fff9546 100644 --- a/util/bash-completion.d/Makefile.am +++ b/util/bash-completion.d/Makefile.am @@ -1,13 +1,117 @@ - bash_completion_source = grub-completion.bash.in bash_completion_script = grub +grub_bios_setup_source = grub-bios-setup.bash.in +grub_bios_setup_script = @grub_bios_setup@ +grub_editenv_source = grub-editenv.bash.in +grub_editenv_script = @grub_editenv@ +grub_install_source = grub-install.bash.in +grub_install_script = @grub_install@ +grub_mkconfig_source = grub-mkconfig.bash.in +grub_mkconfig_script = @grub_mkconfig@ +grub_mkfont_source = grub-mkfont.bash.in +grub_mkfont_script = @grub_mkfont@ +grub_mkimage_source = grub-mkimage.bash.in +grub_mkimage_script = @grub_mkimage@ +grub_mkpasswd_pbkdf2_source = grub-mkpasswd-pbkdf2.bash.in +grub_mkpasswd_pbkdf2_script = @grub_mkpasswd_pbkdf2@ +grub_mkrescue_source = grub-mkrescue.bash.in +grub_mkrescue_script = @grub_mkrescue@ +grub_probe_source = grub-probe.bash.in +grub_probe_script = @grub_probe@ +grub_reboot_source = grub-reboot.bash.in +grub_reboot_script = @grub_reboot@ +grub_script_check_source = grub-script-check.bash.in +grub_script_check_script = @grub_script_check@ +grub_set_default_source = grub-set-default.bash.in +grub_set_default_script = @grub_set_default@ +grub_sparc64_setup_source = grub-sparc64-setup.bash.in +grub_sparc64_setup_script = @grub_sparc64_setup@ -EXTRA_DIST = $(bash_completion_source) +EXTRA_DIST = $(bash_completion_source) \ + $(grub_bios_setup_source) \ + $(grub_editenv_source) \ + $(grub_install_source) \ + $(grub_mkconfig_source) \ + $(grub_mkfont_source) \ + $(grub_mkimage_source) \ + $(grub_mkpasswd_pbkdf2_source) \ + $(grub_mkrescue_source) \ + $(grub_probe_source) \ + $(grub_reboot_source) \ + $(grub_script_check_source) \ + $(grub_set_default_source) \ + $(grub_sparc64_setup_source) -CLEANFILES = $(bash_completion_script) config.log +CLEANFILES = $(bash_completion_script) \ + $(grub_bios_setup_script) \ + $(grub_editenv_script) \ + $(grub_install_script) \ + $(grub_mkconfig_script) \ + $(grub_mkfont_script) \ + $(grub_mkimage_script) \ + $(grub_mkpasswd_pbkdf2_script) \ + $(grub_mkrescure_script) \ + $(grub_probe_script) \ + $(grub_reboot_script) \ + $(grub_script_check_script) \ + $(grub_set_default_script) \ + $(grub_sparc64_setup_script) \ + config.log -bashcompletiondir = $(sysconfdir)/bash_completion.d -bashcompletion_DATA = $(bash_completion_script) +bashcompletiondir = $(datarootdir)/bash-completion/completions +bashcompletion_DATA = $(bash_completion_script) \ + $(grub_bios_setup_script) \ + $(grub_editenv_script) \ + $(grub_install_script) \ + $(grub_mkconfig_script) \ + $(grub_mkfont_script) \ + $(grub_mkimage_script) \ + $(grub_mkpasswd_pbkdf2_script) \ + $(grub_mkrescure_script) \ + $(grub_probe_script) \ + $(grub_reboot_script) \ + $(grub_script_check_script) \ + $(grub_set_default_script) \ + $(grub_sparc64_setup_script) $(bash_completion_script): $(bash_completion_source) $(top_builddir)/config.status $(top_builddir)/config.status --file=$@:$< + +$(grub_bios_setup_script): $(grub_bios_setup_source) $(top_builddir)/config.status + $(top_builddir)/config.status --file=$@:$< + +$(grub_editenv_script): $(grub_editenv_source) $(top_builddir)/config.status + $(top_builddir)/config.status --file=$@:$< + +$(grub_install_script): $(grub_install_source) $(top_builddir)/config.status + $(top_builddir)/config.status --file=$@:$< + +$(grub_mkconfig_script): $(grub_mkconfig_source) $(top_builddir)/config.status + $(top_builddir)/config.status --file=$@:$< + +$(grub_mkfont_script): $(grub_mkfont_source) $(top_builddir)/config.status + $(top_builddir)/config.status --file=$@:$< + +$(grub_mkimage_script): $(grub_mkimage_source) $(top_builddir)/config.status + $(top_builddir)/config.status --file=$@:$< + +$(grub_mkpasswd_pbkdf2_script): $(grub_mkpasswd_pbkdf2_source) $(top_builddir)/config.status + $(top_builddir)/config.status --file=$@:$< + +$(grub_mkrescue_script): $(grub_mkrescue_source) $(top_builddir)/config.status + $(top_builddir)/config.status --file=$@:$< + +$(grub_probe_script): $(grub_probe_source) $(top_builddir)/config.status + $(top_builddir)/config.status --file=$@:$< + +$(grub_reboot_script): $(grub_reboot_source) $(top_builddir)/config.status + $(top_builddir)/config.status --file=$@:$< + +$(grub_script_check_script): $(grub_script_check_source) $(top_builddir)/config.status + $(top_builddir)/config.status --file=$@:$< + +$(grub_set_default_script): $(grub_set_default_source) $(top_builddir)/config.status + $(top_builddir)/config.status --file=$@:$< + +$(grub_sparc64_setup_script): $(grub_sparc64_setup_source) $(top_builddir)/config.status + $(top_builddir)/config.status --file=$@:$< diff --git a/util/bash-completion.d/grub-bios-setup.bash.in b/util/bash-completion.d/grub-bios-setup.bash.in new file mode 100644 index 000000000..2d362b5e2 --- /dev/null +++ b/util/bash-completion.d/grub-bios-setup.bash.in @@ -0,0 +1,30 @@ +# +# Bash completion for @grub-bios-setup@ +# +# Copyright (C) 2024 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 . + +_grub_bios_setup () { + . @datarootdir@/bash-completion/completions/grub && __grub_setup +} +complete -F _grub_bios_setup -o filenames @grub_bios_setup@ + +# Local variables: +# mode: shell-script +# sh-basic-offset: 4 +# sh-indent-comment: t +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et filetype=sh diff --git a/util/bash-completion.d/grub-completion.bash.in b/util/bash-completion.d/grub-completion.bash.in index 213ce1e57..4c88ee901 100644 --- a/util/bash-completion.d/grub-completion.bash.in +++ b/util/bash-completion.d/grub-completion.bash.in @@ -150,7 +150,7 @@ __grub_list_modules () { # # grub-set-default & grub-reboot # -_grub_set_entry () { +__grub_set_entry () { local cur prev split=false COMPREPLY=() @@ -176,21 +176,10 @@ _grub_set_entry () { fi } -__grub_set_default_program="@grub_set_default@" -have ${__grub_set_default_program} && \ - complete -F _grub_set_entry -o filenames ${__grub_set_default_program} -unset __grub_set_default_program - -__grub_reboot_program="@grub_reboot@" -have ${__grub_reboot_program} && \ - complete -F _grub_set_entry -o filenames ${__grub_reboot_program} -unset __grub_reboot_program - - # # grub-editenv # -_grub_editenv () { +__grub_editenv () { local cur prev COMPREPLY=() @@ -208,16 +197,10 @@ _grub_editenv () { create list set unset" } -__grub_editenv_program="@grub_editenv@" -have ${__grub_editenv_program} && \ - complete -F _grub_editenv -o filenames ${__grub_editenv_program} -unset __grub_editenv_program - - # # grub-mkconfig # -_grub_mkconfig () { +__grub_mkconfig () { local cur prev COMPREPLY=() @@ -229,16 +212,11 @@ _grub_mkconfig () { _filedir fi } -__grub_mkconfig_program="@grub_mkconfig@" -have ${__grub_mkconfig_program} && \ - complete -F _grub_mkconfig -o filenames ${__grub_mkconfig_program} -unset __grub_mkconfig_program - # # grub-setup # -_grub_setup () { +__grub_setup () { local cur prev split=false COMPREPLY=() @@ -264,21 +242,10 @@ _grub_setup () { fi } -__grub_bios_setup_program="@grub_bios_setup@" -have ${__grub_bios_setup_program} && \ - complete -F _grub_setup -o filenames ${__grub_bios_setup_program} -unset __grub_bios_setup_program - -__grub_sparc64_setup_program="@grub_sparc64_setup@" -have ${__grub_sparc64_setup_program} && \ - complete -F _grub_setup -o filenames ${__grub_sparc64_setup_program} -unset __grub_sparc64_setup_program - - # # grub-install # -_grub_install () { +__grub_install () { local cur prev last split=false COMPREPLY=() @@ -315,16 +282,11 @@ _grub_install () { _filedir fi } -__grub_install_program="@grub_install@" -have ${__grub_install_program} && \ - complete -F _grub_install -o filenames ${__grub_install_program} -unset __grub_install_program - # # grub-mkfont # -_grub_mkfont () { +__grub_mkfont () { local cur COMPREPLY=() @@ -337,16 +299,11 @@ _grub_mkfont () { _filedir fi } -__grub_mkfont_program="@grub_mkfont@" -have ${__grub_mkfont_program} && \ - complete -F _grub_mkfont -o filenames ${__grub_mkfont_program} -unset __grub_mkfont_program - # # grub-mkrescue # -_grub_mkrescue () { +__grub_mkrescue () { local cur prev last COMPREPLY=() @@ -368,16 +325,11 @@ _grub_mkrescue () { _filedir fi } -__grub_mkrescue_program="@grub_mkrescue@" -have ${__grub_mkrescue_program} && \ - complete -F _grub_mkrescue -o filenames ${__grub_mkrescue_program} -unset __grub_mkrescue_program - # # grub-mkimage # -_grub_mkimage () { +__grub_mkimage () { local cur prev split=false COMPREPLY=() @@ -410,16 +362,11 @@ _grub_mkimage () { _filedir fi } -__grub_mkimage_program="@grub_mkimage@" -have ${__grub_mkimage_program} && \ - complete -F _grub_mkimage -o filenames ${__grub_mkimage_program} -unset __grub_mkimage_program - # # grub-mkpasswd-pbkdf2 # -_grub_mkpasswd_pbkdf2 () { +__grub_mkpasswd_pbkdf2 () { local cur COMPREPLY=() @@ -432,16 +379,11 @@ _grub_mkpasswd_pbkdf2 () { _filedir fi } -__grub_mkpasswd_pbkdf2_program="@grub_mkpasswd_pbkdf2@" -have ${__grub_mkpasswd_pbkdf2_program} && \ - complete -F _grub_mkpasswd_pbkdf2 -o filenames ${__grub_mkpasswd_pbkdf2_program} -unset __grub_mkpasswd_pbkdf2_program - # # grub-probe # -_grub_probe () { +__grub_probe () { local cur prev split=false COMPREPLY=() @@ -470,16 +412,11 @@ _grub_probe () { _filedir fi } -__grub_probe_program="@grub_probe@" -have ${__grub_probe_program} && \ - complete -F _grub_probe -o filenames ${__grub_probe_program} -unset __grub_probe_program - # # grub-script-check # -_grub_script_check () { +__grub_script_check () { local cur COMPREPLY=() @@ -492,10 +429,6 @@ _grub_script_check () { _filedir fi } -__grub_script_check_program="@grub_script_check@" -have ${__grub_script_check_program} && \ - complete -F _grub_script_check -o filenames ${__grub_script_check_program} - # Local variables: # mode: shell-script diff --git a/util/bash-completion.d/grub-editenv.bash.in b/util/bash-completion.d/grub-editenv.bash.in new file mode 100644 index 000000000..29b1333ea --- /dev/null +++ b/util/bash-completion.d/grub-editenv.bash.in @@ -0,0 +1,30 @@ +# +# Bash completion for @grub-editenv@ +# +# Copyright (C) 2024 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 . + +_grub_editenv () { + . @datarootdir@/bash-completion/completions/grub && __grub_editenv +} +complete -F _grub_editenv -o filenames @grub_editenv@ + +# Local variables: +# mode: shell-script +# sh-basic-offset: 4 +# sh-indent-comment: t +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et filetype=sh diff --git a/util/bash-completion.d/grub-install.bash.in b/util/bash-completion.d/grub-install.bash.in new file mode 100644 index 000000000..a89fc614a --- /dev/null +++ b/util/bash-completion.d/grub-install.bash.in @@ -0,0 +1,30 @@ +# +# Bash completion for @grub-install@ +# +# Copyright (C) 2024 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 . + +_grub_install () { + . @datarootdir@/bash-completion/completions/grub && __grub_install +} +complete -F _grub_install -o filenames @grub_install@ + +# Local variables: +# mode: shell-script +# sh-basic-offset: 4 +# sh-indent-comment: t +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et filetype=sh diff --git a/util/bash-completion.d/grub-mkconfig.bash.in b/util/bash-completion.d/grub-mkconfig.bash.in new file mode 100644 index 000000000..862e0c58f --- /dev/null +++ b/util/bash-completion.d/grub-mkconfig.bash.in @@ -0,0 +1,30 @@ +# +# Bash completion for @grub-mkconfig@ +# +# Copyright (C) 2024 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 . + +_grub_mkconfig () { + . @datarootdir@/bash-completion/completions/grub && __grub_mkconfig +} +complete -F _grub_mkconfig -o filenames @grub_mkconfig@ + +# Local variables: +# mode: shell-script +# sh-basic-offset: 4 +# sh-indent-comment: t +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et filetype=sh diff --git a/util/bash-completion.d/grub-mkfont.bash.in b/util/bash-completion.d/grub-mkfont.bash.in new file mode 100644 index 000000000..17baccdf5 --- /dev/null +++ b/util/bash-completion.d/grub-mkfont.bash.in @@ -0,0 +1,30 @@ +# +# Bash completion for @grub-mkfont@ +# +# Copyright (C) 2024 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 . + +_grub_mkfont () { + . @datarootdir@/bash-completion/completions/grub && __grub_mkfont +} +complete -F _grub_mkfont -o filenames @grub_mkfont@ + +# Local variables: +# mode: shell-script +# sh-basic-offset: 4 +# sh-indent-comment: t +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et filetype=sh diff --git a/util/bash-completion.d/grub-mkimage.bash.in b/util/bash-completion.d/grub-mkimage.bash.in new file mode 100644 index 000000000..a383ed3e9 --- /dev/null +++ b/util/bash-completion.d/grub-mkimage.bash.in @@ -0,0 +1,30 @@ +# +# Bash completion for @grub-mkimage@ +# +# Copyright (C) 2024 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 . + +_grub_mkimage () { + . @datarootdir@/bash-completion/completions/grub && __grub_mkimage +} +complete -F _grub_mkimage -o filenames @grub_mkimage@ + +# Local variables: +# mode: shell-script +# sh-basic-offset: 4 +# sh-indent-comment: t +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et filetype=sh diff --git a/util/bash-completion.d/grub-mkpasswd-pbkdf2.bash.in b/util/bash-completion.d/grub-mkpasswd-pbkdf2.bash.in new file mode 100644 index 000000000..32b8fd6eb --- /dev/null +++ b/util/bash-completion.d/grub-mkpasswd-pbkdf2.bash.in @@ -0,0 +1,30 @@ +# +# Bash completion for @grub-mkpasswd-pbkdf2@ +# +# Copyright (C) 2024 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 . + +_grub_mkpasswd_pbkdf2 () { + . @datarootdir@/bash-completion/completions/grub && __grub_mkpasswd_pbkdf2 +} +complete -F _grub_mkpasswd_pbkdf2 -o filenames @grub_mkpasswd_pbkdf2@ + +# Local variables: +# mode: shell-script +# sh-basic-offset: 4 +# sh-indent-comment: t +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et filetype=sh diff --git a/util/bash-completion.d/grub-mkrescue.bash.in b/util/bash-completion.d/grub-mkrescue.bash.in new file mode 100644 index 000000000..5968ba00e --- /dev/null +++ b/util/bash-completion.d/grub-mkrescue.bash.in @@ -0,0 +1,30 @@ +# +# Bash completion for @grub-mkresue@ +# +# Copyright (C) 2024 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 . + +_grub_mkrescue () { + . @datarootdir@/bash-completion/completions/grub && __grub_mkrescue +} +complete -F _grub_mkrescue -o filenames @grub_mkrescue@ + +# Local variables: +# mode: shell-script +# sh-basic-offset: 4 +# sh-indent-comment: t +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et filetype=sh diff --git a/util/bash-completion.d/grub-probe.bash.in b/util/bash-completion.d/grub-probe.bash.in new file mode 100644 index 000000000..08400f2f1 --- /dev/null +++ b/util/bash-completion.d/grub-probe.bash.in @@ -0,0 +1,30 @@ +# +# Bash completion for @grub-probe@ +# +# Copyright (C) 2024 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 . + +_grub_probe () { + . @datarootdir@/bash-completion/completions/grub && __grub_probe +} +complete -F _grub_probe -o filenames @grub_probe@ + +# Local variables: +# mode: shell-script +# sh-basic-offset: 4 +# sh-indent-comment: t +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et filetype=sh diff --git a/util/bash-completion.d/grub-reboot.bash.in b/util/bash-completion.d/grub-reboot.bash.in new file mode 100644 index 000000000..154aecea9 --- /dev/null +++ b/util/bash-completion.d/grub-reboot.bash.in @@ -0,0 +1,30 @@ +# +# Bash completion for @grub-reboot@ +# +# Copyright (C) 2024 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 . + +_grub_reboot () { + . @datarootdir@/bash-completion/completions/grub && __grub_set_entry +} +complete -F _grub_reboot -o filenames @grub_reboot@ + +# Local variables: +# mode: shell-script +# sh-basic-offset: 4 +# sh-indent-comment: t +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et filetype=sh diff --git a/util/bash-completion.d/grub-script-check.bash.in b/util/bash-completion.d/grub-script-check.bash.in new file mode 100644 index 000000000..22d376832 --- /dev/null +++ b/util/bash-completion.d/grub-script-check.bash.in @@ -0,0 +1,30 @@ +# +# Bash completion for @grub-script-check@ +# +# Copyright (C) 2024 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 . + +_grub_script_check () { + . @datarootdir@/bash-completion/completions/grub && __grub_script_check +} +complete -F _grub_script_check -o filenames @grub_script_check@ + +# Local variables: +# mode: shell-script +# sh-basic-offset: 4 +# sh-indent-comment: t +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et filetype=sh diff --git a/util/bash-completion.d/grub-set-default.bash.in b/util/bash-completion.d/grub-set-default.bash.in new file mode 100644 index 000000000..14501b4fb --- /dev/null +++ b/util/bash-completion.d/grub-set-default.bash.in @@ -0,0 +1,30 @@ +# +# Bash completion for @grub-set-default@ +# +# Copyright (C) 2024 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 . + +_grub_set_default () { + . @datarootdir@/bash-completion/completions/grub && __grub_set_entry +} +complete -F _grub_set_default -o filenames @grub_set_default@ + +# Local variables: +# mode: shell-script +# sh-basic-offset: 4 +# sh-indent-comment: t +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et filetype=sh diff --git a/util/bash-completion.d/grub-sparc64-setup.bash.in b/util/bash-completion.d/grub-sparc64-setup.bash.in new file mode 100644 index 000000000..6123d7b7c --- /dev/null +++ b/util/bash-completion.d/grub-sparc64-setup.bash.in @@ -0,0 +1,30 @@ +# +# Bash completion for @grub-sparc64-setup@ +# +# Copyright (C) 2024 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 . + +_grub_sparc64_setup () { + . @datarootdir@/bash-completion/completions/grub && __grub_setup +} +complete -F _grub_sparc64_setup -o filenames @grub_sparc64_setup@ + +# Local variables: +# mode: shell-script +# sh-basic-offset: 4 +# sh-indent-comment: t +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et filetype=sh From 04d2a50f31c4f29a451b507aefe02bf0798b534b Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Tue, 30 Jan 2024 13:59:43 +0100 Subject: [PATCH 120/402] Revert "templates: Reinstate unused version comparison functions with warning" We reinstated these functions before the 2.12 release with a warning such that users upgrading to 2.12 who had custom scripts using them would not get broken in the upgrade and agreed to remove them after the 2.12 release. This removes them accordingly. This reverts commit e7a831963 (templates: Reinstate unused version comparison functions with warning). Cc: Mathieu Desnoyers Cc: Daniel Kiper Signed-off-by: Julian Andres Klode Reviewed-by: Daniel Kiper --- util/grub-mkconfig_lib.in | 54 --------------------------------------- 1 file changed, 54 deletions(-) diff --git a/util/grub-mkconfig_lib.in b/util/grub-mkconfig_lib.in index 33e1750ae..08953287c 100644 --- a/util/grub-mkconfig_lib.in +++ b/util/grub-mkconfig_lib.in @@ -244,60 +244,6 @@ grub_move_to_front () done } -version_test_numeric () -{ - grub_warn "version_test_numeric() is deprecated. Use version_sort() instead." - version_test_numeric_a="$1" - version_test_numeric_cmp="$2" - version_test_numeric_b="$3" - if [ "$version_test_numeric_a" = "$version_test_numeric_b" ] ; then - case "$version_test_numeric_cmp" in - ge|eq|le) return 0 ;; - gt|lt) return 1 ;; - esac - fi - if [ "$version_test_numeric_cmp" = "lt" ] ; then - version_test_numeric_c="$version_test_numeric_a" - version_test_numeric_a="$version_test_numeric_b" - version_test_numeric_b="$version_test_numeric_c" - fi - if (echo "$version_test_numeric_a" ; echo "$version_test_numeric_b") | version_sort | head -n 1 | grep -qx "$version_test_numeric_b" ; then - return 0 - else - return 1 - fi -} - -version_test_gt () -{ - grub_warn "version_test_gt() is deprecated. Use version_sort() instead." - version_test_gt_a="`echo "$1" | sed -e "s/[^-]*-//"`" - version_test_gt_b="`echo "$2" | sed -e "s/[^-]*-//"`" - version_test_gt_cmp=gt - if [ "x$version_test_gt_b" = "x" ] ; then - return 0 - fi - case "$version_test_gt_a:$version_test_gt_b" in - *.old:*.old) ;; - *.old:*) version_test_gt_a="`echo "$version_test_gt_a" | sed -e 's/\.old$//'`" ; version_test_gt_cmp=gt ;; - *:*.old) version_test_gt_b="`echo "$version_test_gt_b" | sed -e 's/\.old$//'`" ; version_test_gt_cmp=ge ;; - esac - version_test_numeric "$version_test_gt_a" "$version_test_gt_cmp" "$version_test_gt_b" - return "$?" -} - -version_find_latest () -{ - grub_warn "version_find_latest() is deprecated. Use version_sort() instead." - version_find_latest_a="" - for i in "$@" ; do - if version_test_gt "$i" "$version_find_latest_a" ; then - version_find_latest_a="$i" - fi - done - echo "$version_find_latest_a" -} - # One layer of quotation is eaten by "" and the second by sed; so this turns # ' into \'. grub_quote () { From 68dd65cfdaad08b1f8ec01b84949b0bf88bc0d8c Mon Sep 17 00:00:00 2001 From: Jon DeVree Date: Sun, 11 Feb 2024 10:34:58 -0500 Subject: [PATCH 121/402] fs/xfs: Handle non-continuous data blocks in directory extents The directory extent list does not have to be a continuous list of data blocks. When GRUB tries to read a non-existant member of the list, grub_xfs_read_file() will return a block of zero'ed memory. Checking for a zero'ed magic number is sufficient to skip this non-existant data block. Prior to commit 07318ee7e (fs/xfs: Fix XFS directory extent parsing) this was handled as a subtle side effect of reading the (non-existant) tail data structure. Since the block was zero'ed the computation of the number of directory entries in the block would return 0 as well. Fixes: 07318ee7e (fs/xfs: Fix XFS directory extent parsing) Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=2254370 Signed-off-by: Jon DeVree Reviewed-By: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/fs/xfs.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index bc2224dbb..8e02ab4a3 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -902,6 +902,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, grub_xfs_first_de(dir->data, dirblock); int entries = -1; char *end = dirblock + dirblk_size; + grub_uint32_t magic; numread = grub_xfs_read_file (dir, 0, 0, blk << dirblk_log2, @@ -912,6 +913,15 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, return 0; } + /* + * If this data block isn't actually part of the extent list then + * grub_xfs_read_file() returns a block of zeros. So, if the magic + * number field is all zeros then this block should be skipped. + */ + magic = *(grub_uint32_t *)(void *) dirblock; + if (!magic) + continue; + /* * Leaf and tail information are only in the data block if the number * of extents is 1. From 39c927df66c7ca62d97905d1385054ac9ce67209 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Mon, 19 Feb 2024 11:29:11 +0800 Subject: [PATCH 122/402] gfxmenu/view: Resolve false grub_errno disrupting boot process When enabling gfxmenu and choosing to boot the Xen hypervisor from its menu, an error occurred: error: ../../grub-core/video/bitmap_scale.c:42:null src bitmap in grub_video_create_scaled. The error is returned by grub_video_bitmap_create_scaled() when the source pixmap is not there. The init_background() uses it to scale up the background image so it can fully fit into the screen resolution. However not all backgrounds are set by a image, i.e. the "desktop-image" property of the theme file. Instead a color code may be used, for example OpenSUSE's green background uses "desktop-color" property: desktop-color: "#0D202F" So it is absolutely fine to call init_background() without a raw pixmap if color code is used. A missing check has to be added to ensure the grub_errno will not be erroneously set and gets in the way of ensuing boot process. The reason it happens sporadically is due to grub_errno is reset to GRUB_ERR_NONE in other places if a function's error return can be ignored. In particular this hunk in grub_gfxmenu_create_box() does the majority of the reset of grub_errno returned by init_background(), but the path may not be always chosen. grub_video_bitmap_load (&box->raw_pixmaps[i], path); grub_free (path); /* Ignore missing pixmaps. */ grub_errno = GRUB_ERR_NONE; In any case, we cannot account on such random behavior and should only return grub_errno if it is justified. On the occasion move the grub_video_bitmap struct definition to the beginning of the function. Signed-off-by: Michael Chang Reviewed-by: Daniel Kiper --- grub-core/gfxmenu/view.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/grub-core/gfxmenu/view.c b/grub-core/gfxmenu/view.c index 6358004b2..e02eba8b0 100644 --- a/grub-core/gfxmenu/view.c +++ b/grub-core/gfxmenu/view.c @@ -553,10 +553,18 @@ init_terminal (grub_gfxmenu_view_t view) static void init_background (grub_gfxmenu_view_t view) { + struct grub_video_bitmap *scaled_bitmap; + + /* + * You don't have to scale a raw image if it's not present. This prevents + * setting grub_errno and disrupting a command execution. + */ + if (view->raw_desktop_image == NULL) + return; + if (view->scaled_desktop_image) return; - struct grub_video_bitmap *scaled_bitmap; if (view->desktop_image_scale_method == GRUB_VIDEO_BITMAP_SELECTION_METHOD_STRETCH) grub_video_bitmap_create_scaled (&scaled_bitmap, From aa80270154d8593496a1f80398c1c66ddf87d635 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 15 Mar 2024 22:02:07 +0300 Subject: [PATCH 123/402] acpi: Mark MADT entries as packed No alignment is guaranteed and in fact on my IA-64 SAPIC is aligned to 4 bytes instead of 8 and causes a trap. It affects only rarely used lsacpi command and so went unnoticed. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- include/grub/acpi.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/include/grub/acpi.h b/include/grub/acpi.h index 17aadb802..f2862da74 100644 --- a/include/grub/acpi.h +++ b/include/grub/acpi.h @@ -81,6 +81,7 @@ struct grub_acpi_fadt #define GRUB_ACPI_MADT_SIGNATURE "APIC" +/* Note: here GRUB_PACKED is not needed because we have grub_uint8_t only. */ struct grub_acpi_madt_entry_header { grub_uint8_t type; @@ -112,7 +113,7 @@ struct grub_acpi_madt_entry_lapic grub_uint8_t acpiid; grub_uint8_t apicid; grub_uint32_t flags; -}; +} GRUB_PACKED; struct grub_acpi_madt_entry_ioapic { @@ -121,7 +122,7 @@ struct grub_acpi_madt_entry_ioapic grub_uint8_t pad; grub_uint32_t address; grub_uint32_t global_sys_interrupt; -}; +} GRUB_PACKED; struct grub_acpi_madt_entry_interrupt_override { @@ -148,7 +149,7 @@ struct grub_acpi_madt_entry_sapic grub_uint8_t pad; grub_uint32_t global_sys_interrupt_base; grub_uint64_t addr; -}; +} GRUB_PACKED; struct grub_acpi_madt_entry_lsapic { @@ -160,7 +161,7 @@ struct grub_acpi_madt_entry_lsapic grub_uint32_t flags; grub_uint32_t cpu_uid; grub_uint8_t cpu_uid_str[0]; -}; +} GRUB_PACKED; struct grub_acpi_madt_entry_platform_int_source { @@ -172,7 +173,7 @@ struct grub_acpi_madt_entry_platform_int_source grub_uint8_t sapic_vector; grub_uint32_t global_sys_int; grub_uint32_t src_flags; -}; +} GRUB_PACKED; enum { From 52e039e00be77016a752ff77ee7e6e1d4a3f1c00 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 15 Mar 2024 22:35:07 +0300 Subject: [PATCH 124/402] efi: Enable CMOS on x86 EFI platforms The CMOS actually exists on most EFI platforms and in some cases is used to store useful data that makes it justifiable for GRUB to read/write it. As for date and time keep using EFI API and not CMOS one. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/Makefile.core.def | 4 ++++ grub-core/commands/i386/cmostest.c | 6 +++--- include/grub/x86_64/cmos.h | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 include/grub/x86_64/cmos.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 1571421d7..007ff628e 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -714,12 +714,16 @@ module = { name = cmostest; common = commands/i386/cmostest.c; enable = cmos; + enable = i386_efi; + enable = x86_64_efi; }; module = { name = cmosdump; common = commands/i386/cmosdump.c; enable = cmos; + enable = i386_efi; + enable = x86_64_efi; }; module = { diff --git a/grub-core/commands/i386/cmostest.c b/grub-core/commands/i386/cmostest.c index 9f6b56a2f..1f0c5341d 100644 --- a/grub-core/commands/i386/cmostest.c +++ b/grub-core/commands/i386/cmostest.c @@ -104,13 +104,13 @@ static grub_command_t cmd, cmd_clean, cmd_set; GRUB_MOD_INIT(cmostest) { - cmd = grub_register_command ("cmostest", grub_cmd_cmostest, + cmd = grub_register_command_lockdown ("cmostest", grub_cmd_cmostest, N_("BYTE:BIT"), N_("Test bit at BYTE:BIT in CMOS.")); - cmd_clean = grub_register_command ("cmosclean", grub_cmd_cmosclean, + cmd_clean = grub_register_command_lockdown ("cmosclean", grub_cmd_cmosclean, N_("BYTE:BIT"), N_("Clear bit at BYTE:BIT in CMOS.")); - cmd_set = grub_register_command ("cmosset", grub_cmd_cmosset, + cmd_set = grub_register_command_lockdown ("cmosset", grub_cmd_cmosset, N_("BYTE:BIT"), /* TRANSLATORS: A bit may be either set (1) or clear (0). */ N_("Set bit at BYTE:BIT in CMOS.")); diff --git a/include/grub/x86_64/cmos.h b/include/grub/x86_64/cmos.h new file mode 100644 index 000000000..03722f805 --- /dev/null +++ b/include/grub/x86_64/cmos.h @@ -0,0 +1 @@ +#include From 28c4405208cfb6e2cea737f6cbaf17e631bac6cd Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 15 Mar 2024 22:59:04 +0300 Subject: [PATCH 125/402] util/grub-fstest: Add a new command zfs-bootfs It is useful to check zfs-bootfs command. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- util/grub-fstest.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/util/grub-fstest.c b/util/grub-fstest.c index 25ae52ab6..7ff9037b8 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -69,7 +69,8 @@ enum { CMD_BLOCKLIST, CMD_TESTLOAD, CMD_ZFSINFO, - CMD_XNU_UUID + CMD_XNU_UUID, + CMD_ZFS_BOOTFS }; #define BUF_SIZE 32256 @@ -441,6 +442,9 @@ fstest (int n) case CMD_ZFSINFO: execute_command ("zfsinfo", n, args); break; + case CMD_ZFS_BOOTFS: + execute_command ("zfs-bootfs", n, args); + break; case CMD_CP: cmd_cp (args[0], args[1]); break; @@ -518,6 +522,7 @@ static struct argp_option options[] = { {N_("crc FILE"), 0, 0 , OPTION_DOC, N_("Get crc32 checksum of FILE."), 1}, {N_("blocklist FILE"), 0, 0, OPTION_DOC, N_("Display blocklist of FILE."), 1}, {N_("xnu_uuid DEVICE"), 0, 0, OPTION_DOC, N_("Compute XNU UUID of the device."), 1}, + {N_("zfs-bootfs ZFS_DATASET"), 0, 0, OPTION_DOC, N_("Compute ZFS dataset bootpath."), 1}, {"root", 'r', N_("DEVICE_NAME"), 0, N_("Set root device."), 2}, {"skip", 's', N_("NUM"), 0, N_("Skip N bytes from output file."), 2}, @@ -712,6 +717,11 @@ argp_parser (int key, char *arg, struct argp_state *state) cmd = CMD_XNU_UUID; nparm = 0; } + else if (grub_strcmp (arg, "zfs-bootfs") == 0) + { + cmd = CMD_ZFS_BOOTFS; + nparm = 0; + } else { fprintf (stderr, _("Invalid command %s.\n"), arg); From 0876fdf215292a06ad087f862ae7677c85ae444f Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Mon, 25 Mar 2024 10:11:34 +0800 Subject: [PATCH 126/402] util/bash-completion: Fix for bash-completion 2.12 _split_longopt() was the bash-completion private API and removed since bash-completion 2.12. This commit initializes the bash-completion general variables with _init_completion() to avoid the potential "command not found" error. Although bash-completion 2.12 introduces _comp_initialize() to deprecate _init_completion(), _init_completion() is still chosen for the better backward compatibility. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- .../bash-completion.d/grub-completion.bash.in | 61 +++++++------------ 1 file changed, 22 insertions(+), 39 deletions(-) diff --git a/util/bash-completion.d/grub-completion.bash.in b/util/bash-completion.d/grub-completion.bash.in index 4c88ee901..749a5d3cf 100644 --- a/util/bash-completion.d/grub-completion.bash.in +++ b/util/bash-completion.d/grub-completion.bash.in @@ -151,13 +151,10 @@ __grub_list_modules () { # grub-set-default & grub-reboot # __grub_set_entry () { - local cur prev split=false + local cur prev words cword split + _init_completion -s || return COMPREPLY=() - cur=`_get_cword` - prev=${COMP_WORDS[COMP_CWORD-1]} - - _split_longopt && split=true case "$prev" in --boot-directory) @@ -180,11 +177,10 @@ __grub_set_entry () { # grub-editenv # __grub_editenv () { - local cur prev + local cur prev words cword + _init_completion || return COMPREPLY=() - cur=`_get_cword` - prev=${COMP_WORDS[COMP_CWORD-1]} case "$prev" in create|list|set|unset) @@ -201,10 +197,10 @@ __grub_editenv () { # grub-mkconfig # __grub_mkconfig () { - local cur prev + local cur prev words cword + _init_completion || return COMPREPLY=() - cur=`_get_cword` if [[ "$cur" == -* ]]; then __grubcomp "$(__grub_get_options_from_help)" @@ -217,13 +213,10 @@ __grub_mkconfig () { # grub-setup # __grub_setup () { - local cur prev split=false + local cur prev words cword split + _init_completion -s || return COMPREPLY=() - cur=`_get_cword` - prev=${COMP_WORDS[COMP_CWORD-1]} - - _split_longopt && split=true case "$prev" in -d|--directory) @@ -246,15 +239,12 @@ __grub_setup () { # grub-install # __grub_install () { - local cur prev last split=false + local cur prev words cword split last + _init_completion -s || return COMPREPLY=() - cur=`_get_cword` - prev=${COMP_WORDS[COMP_CWORD-1]} last=$(__grub_get_last_option) - _split_longopt && split=true - case "$prev" in --boot-directory) _filedir -d @@ -287,10 +277,10 @@ __grub_install () { # grub-mkfont # __grub_mkfont () { - local cur + local cur prev words cword + _init_completion || return COMPREPLY=() - cur=`_get_cword` if [[ "$cur" == -* ]]; then __grubcomp "$(__grub_get_options_from_help)" @@ -304,11 +294,10 @@ __grub_mkfont () { # grub-mkrescue # __grub_mkrescue () { - local cur prev last + local cur prev words cword last + _init_completion || return COMPREPLY=() - cur=`_get_cword` - prev=${COMP_WORDS[COMP_CWORD-1]} last=$(__grub_get_last_option) if [[ "$cur" == -* ]]; then @@ -330,13 +319,10 @@ __grub_mkrescue () { # grub-mkimage # __grub_mkimage () { - local cur prev split=false + local cur prev words cword split + _init_completion -s || return COMPREPLY=() - cur=`_get_cword` - prev=${COMP_WORDS[COMP_CWORD-1]} - - _split_longopt && split=true case "$prev" in -d|--directory|-p|--prefix) @@ -367,10 +353,10 @@ __grub_mkimage () { # grub-mkpasswd-pbkdf2 # __grub_mkpasswd_pbkdf2 () { - local cur + local cur prev words cword + _init_completion || return COMPREPLY=() - cur=`_get_cword` if [[ "$cur" == -* ]]; then __grubcomp "$(__grub_get_options_from_help)" @@ -384,13 +370,10 @@ __grub_mkpasswd_pbkdf2 () { # grub-probe # __grub_probe () { - local cur prev split=false + local cur prev words cword split + _init_completion -s || return COMPREPLY=() - cur=`_get_cword` - prev=${COMP_WORDS[COMP_CWORD-1]} - - _split_longopt && split=true case "$prev" in -t|--target) @@ -417,10 +400,10 @@ __grub_probe () { # grub-script-check # __grub_script_check () { - local cur + local cur prev words cword + _init_completion || return COMPREPLY=() - cur=`_get_cword` if [[ "$cur" == -* ]]; then __grubcomp "$(__grub_get_options_from_help)" From 8719cc2040368d43ab2de0b6e1b850b2c9cfc5b7 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Tue, 9 Apr 2024 19:56:02 +0200 Subject: [PATCH 127/402] windows: Add _stack_chk_guard/_stack_chk_fail symbols for Windows 64-bit target Otherwise the GRUB cannot start due to missing symbols when stack protector is enabled on EFI platforms. Signed-off-by: Daniel Kiper Reviewed-by: Vladimir Serbinenko --- include/grub/stack_protector.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/grub/stack_protector.h b/include/grub/stack_protector.h index c88dc00b5..13d2657d9 100644 --- a/include/grub/stack_protector.h +++ b/include/grub/stack_protector.h @@ -25,6 +25,10 @@ #ifdef GRUB_STACK_PROTECTOR extern grub_addr_t EXPORT_VAR (__stack_chk_guard); extern void __attribute__ ((noreturn)) EXPORT_FUNC (__stack_chk_fail) (void); +#if defined(_WIN64) && !defined(__CYGWIN__) /* MinGW, Windows 64-bit target. */ +static grub_addr_t __attribute__ ((weakref("__stack_chk_guard"))) EXPORT_VAR (_stack_chk_guard); +static void __attribute__ ((noreturn, weakref("__stack_chk_fail"))) EXPORT_FUNC (_stack_chk_fail) (void); +#endif #endif #endif /* GRUB_STACK_PROTECTOR_H */ From 6744840b17822b0c9b15ea8d0130429d133a22f2 Mon Sep 17 00:00:00 2001 From: Oliver Steffen Date: Thu, 22 Feb 2024 14:32:31 +0100 Subject: [PATCH 128/402] build: Track explicit module dependencies in Makefile.core.def Add a new keyword, "depends", to the module definition syntax used in Makefile.core.def. This allows specifying explicit module dependencies together with the module definition. Do not track the "extra_deps.lst" file in the repository anymore, it is now auto-generated. Make use of this new keyword in the bli module definition. Signed-off-by: Oliver Steffen Reviewed-by: Daniel Kiper --- .gitignore | 1 - conf/Makefile.common | 1 + gentpl.py | 7 +++++++ grub-core/Makefile.am | 9 ++++++--- grub-core/Makefile.core.def | 1 + grub-core/extra_deps.lst | 1 - grub-core/genmoddep.awk | 2 +- 7 files changed, 16 insertions(+), 6 deletions(-) delete mode 100644 grub-core/extra_deps.lst diff --git a/.gitignore b/.gitignore index 4d0dfb700..11fcecf5c 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,6 @@ *.img *.log *.lst -!/grub-core/extra_deps.lst *.marker *.mod *.o diff --git a/conf/Makefile.common b/conf/Makefile.common index b8f216f6c..c60f55386 100644 --- a/conf/Makefile.common +++ b/conf/Makefile.common @@ -111,6 +111,7 @@ MOD_FILES = MODULE_FILES = MARKER_FILES = KERNEL_HEADER_FILES = +EXTRA_DEPS = bin_SCRIPTS = bin_PROGRAMS = diff --git a/gentpl.py b/gentpl.py index bfab2113a..3b12eca6c 100644 --- a/gentpl.py +++ b/gentpl.py @@ -631,6 +631,9 @@ def platform_values(defn, platform, suffix): def extra_dist(defn): return foreach_value(defn, "extra_dist", lambda value: value + " ") +def extra_dep(defn): + return foreach_value(defn, "depends", lambda value: value + " ") + def platform_sources(defn, p): return platform_values(defn, p, "") def platform_nodist_sources(defn, p): return platform_values(defn, p, "_nodist") @@ -699,6 +702,10 @@ def module(defn, platform): gvar_add("MOD_FILES", name + ".mod") gvar_add("MARKER_FILES", name + ".marker") gvar_add("CLEANFILES", name + ".marker") + + for dep in defn.find_all("depends"): + gvar_add("EXTRA_DEPS", "depends " + name + " " + dep + ":") + output(""" """ + name + """.marker: $(""" + cname(defn) + """_SOURCES) $(nodist_""" + cname(defn) + """_SOURCES) $(TARGET_CPP) -DGRUB_LST_GENERATOR $(CPPFLAGS_MARKER) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(""" + cname(defn) + """_CPPFLAGS) $(CPPFLAGS) $^ > $@.new || (rm -f $@; exit 1) diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index f18550c1c..1eda467e0 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -454,8 +454,11 @@ crypto.lst: $(srcdir)/lib/libgcrypt-grub/cipher/crypto.lst platform_DATA += crypto.lst CLEANFILES += crypto.lst -syminfo.lst: gensyminfo.sh kernel_syms.lst $(top_srcdir)/grub-core/extra_deps.lst $(MODULE_FILES) - cat kernel_syms.lst $(top_srcdir)/grub-core/extra_deps.lst > $@.new +extra_deps.lst: + @echo $(EXTRA_DEPS) | sed "s/\s*:\s*/\n/g" > $@ + +syminfo.lst: gensyminfo.sh kernel_syms.lst extra_deps.lst $(MODULE_FILES) + cat kernel_syms.lst extra_deps.lst > $@.new for m in $(MODULE_FILES); do \ sh $< $$m >> $@.new || exit 1; \ done @@ -465,7 +468,7 @@ syminfo.lst: gensyminfo.sh kernel_syms.lst $(top_srcdir)/grub-core/extra_deps.ls moddep.lst: syminfo.lst genmoddep.awk video.lst cat $< | sort | $(AWK) -f $(srcdir)/genmoddep.awk > $@ || (rm -f $@; exit 1) platform_DATA += moddep.lst -CLEANFILES += config.log syminfo.lst moddep.lst +CLEANFILES += config.log syminfo.lst moddep.lst extra_deps.lst $(MOD_FILES): %.mod : genmod.sh moddep.lst %.module$(EXEEXT) build-grub-module-verifier$(BUILD_EXEEXT) TARGET_OBJ2ELF=@TARGET_OBJ2ELF@ sh $^ $@ diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 007ff628e..8e1b1d9f3 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -2598,4 +2598,5 @@ module = { name = bli; efi = commands/bli.c; enable = efi; + depends = part_gpt; }; diff --git a/grub-core/extra_deps.lst b/grub-core/extra_deps.lst deleted file mode 100644 index f44ad6a0c..000000000 --- a/grub-core/extra_deps.lst +++ /dev/null @@ -1 +0,0 @@ -depends bli part_gpt diff --git a/grub-core/genmoddep.awk b/grub-core/genmoddep.awk index cc987a53a..ab457cb2b 100644 --- a/grub-core/genmoddep.awk +++ b/grub-core/genmoddep.awk @@ -35,7 +35,7 @@ BEGIN { for (i = 3; i <= NF; i++) { modtab[$2] = modtab[$2] " " $i; } - } + } else if ($1 == "") {} #Skip empty lines else { printf "error: %u: unrecognized input format\n", NR >"/dev/stderr"; error++; From b272ed230eb9269a37a79b1ad760e1e233eb9916 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sat, 27 Apr 2024 08:07:58 -0500 Subject: [PATCH 129/402] efi: Fix stack protector issues The "ground truth" stack protector cookie value is kept in a global variable, and loaded in every function prologue and epilogue to store it into resp. compare it with the stack slot holding the cookie. If the comparison fails, the program aborts, and this might occur spuriously when the global variable changes values between the entry and exit of a function. This implies that assigning the global variable at boot should not involve any instrumented function calls, unless special care is taken to ensure that the live call stack is synchronized, which is non-trivial. So avoid any function calls, including grub_memcpy(), which is unnecessary given that the stack cookie is always a suitably aligned variable of the native word size. While at it, leave the last byte 0x0 to avoid inadvertent unbounded strings on the stack. Note that the use of __attribute__((optimize)) is described as unsuitable for production use in the GCC documentation, so let's drop this as well now that it is no longer needed. Signed-off-by: Ard Biesheuvel Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- grub-core/kern/efi/init.c | 27 ++++++++------------------- grub-core/kern/main.c | 11 +++++++++++ include/grub/stack_protector.h | 12 ++++++++++++ 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c index 6c54af6e7..1637077e1 100644 --- a/grub-core/kern/efi/init.c +++ b/grub-core/kern/efi/init.c @@ -39,12 +39,6 @@ static grub_efi_char16_t stack_chk_fail_msg[] = static grub_guid_t rng_protocol_guid = GRUB_EFI_RNG_PROTOCOL_GUID; -/* - * Don't put this on grub_efi_init()'s local stack to avoid it - * getting a stack check. - */ -static grub_efi_uint8_t stack_chk_guard_buf[32]; - /* Initialize canary in case there is no RNG protocol. */ grub_addr_t __stack_chk_guard = (grub_addr_t) GRUB_STACK_PROTECTOR_INIT; @@ -77,8 +71,8 @@ __stack_chk_fail (void) while (1); } -static void -stack_protector_init (void) +grub_addr_t +grub_stack_protector_init (void) { grub_efi_rng_protocol_t *rng; @@ -87,23 +81,20 @@ stack_protector_init (void) if (rng != NULL) { grub_efi_status_t status; + grub_addr_t guard = 0; - status = rng->get_rng (rng, NULL, sizeof (stack_chk_guard_buf), - stack_chk_guard_buf); + status = rng->get_rng (rng, NULL, sizeof (guard) - 1, + (grub_efi_uint8_t *) &guard); if (status == GRUB_EFI_SUCCESS) - grub_memcpy (&__stack_chk_guard, stack_chk_guard_buf, sizeof (__stack_chk_guard)); + return guard; } -} -#else -static void -stack_protector_init (void) -{ + return 0; } #endif grub_addr_t grub_modbase; -__attribute__ ((__optimize__ ("-fno-stack-protector"))) void +void grub_efi_init (void) { grub_modbase = grub_efi_section_addr ("mods"); @@ -111,8 +102,6 @@ grub_efi_init (void) messages. */ grub_console_init (); - stack_protector_init (); - /* Initialize the memory management system. */ grub_efi_mm_init (); diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c index 731c07c29..744197785 100644 --- a/grub-core/kern/main.c +++ b/grub-core/kern/main.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -265,6 +266,16 @@ reclaim_module_space (void) void __attribute__ ((noreturn)) grub_main (void) { +#ifdef GRUB_STACK_PROTECTOR + /* + * This call should only be made from a function that does not return because + * functions that return will get instrumented to check that the stack cookie + * does not change and this call will change the stack cookie. Thus a stack + * guard failure will be triggered. + */ + grub_update_stack_guard (); +#endif + /* First of all, initialize the machine. */ grub_machine_init (); diff --git a/include/grub/stack_protector.h b/include/grub/stack_protector.h index 13d2657d9..e4849b2a0 100644 --- a/include/grub/stack_protector.h +++ b/include/grub/stack_protector.h @@ -29,6 +29,18 @@ extern void __attribute__ ((noreturn)) EXPORT_FUNC (__stack_chk_fail) (void); static grub_addr_t __attribute__ ((weakref("__stack_chk_guard"))) EXPORT_VAR (_stack_chk_guard); static void __attribute__ ((noreturn, weakref("__stack_chk_fail"))) EXPORT_FUNC (_stack_chk_fail) (void); #endif + +extern grub_addr_t grub_stack_protector_init (void); + +static inline __attribute__((__always_inline__)) +void grub_update_stack_guard (void) +{ + grub_addr_t guard; + + guard = grub_stack_protector_init (); + if (guard) + __stack_chk_guard = guard; +} #endif #endif /* GRUB_STACK_PROTECTOR_H */ From 99b4c0c3841bf71b0f2ef83607e9d6f13874c67c Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Mon, 29 Apr 2024 16:38:03 +0000 Subject: [PATCH 130/402] disk/mdraid1x_linux: Prevent infinite recursion The test corpus for version-1 RAID generated an infinite recursion in grub_partition_iterate() while attempting to read the superblock. The reason for the issue was that the data region overlapped with the superblock. The infinite call loop looks like this: grub_partition_iterate() -> partmap->iterate() -> -> grub_disk_read() -> grub_disk_read_small() -> -> grub_disk_read_small_real() -> grub_diskfilter_read() -> -> read_lv() -> read_segment() -> grub_diskfilter_read_node() -> -> grub_disk_read() -> grub_disk_read_small() -> ... The fix adds checks for both the superblock region and the data region when parsing the superblock metadata in grub_mdraid_detect(). Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper --- grub-core/disk/mdraid1x_linux.c | 78 +++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/grub-core/disk/mdraid1x_linux.c b/grub-core/disk/mdraid1x_linux.c index 72e5cb6f4..dd5d440a3 100644 --- a/grub-core/disk/mdraid1x_linux.c +++ b/grub-core/disk/mdraid1x_linux.c @@ -23,6 +23,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -103,6 +104,9 @@ struct grub_raid_super_1x #define WriteMostly1 1 /* Mask for writemostly flag in above devflags. */ +#define GRUB_MD_SECTOR_SHIFT 9 /* Follow Linux kernel v6.8. */ +#define GRUB_MD_SECTOR_SIZE (1 << GRUB_MD_SECTOR_SHIFT) + static struct grub_diskfilter_vg * grub_mdraid_detect (grub_disk_t disk, struct grub_diskfilter_pv_id *id, @@ -129,6 +133,7 @@ grub_mdraid_detect (grub_disk_t disk, grub_uint32_t level; struct grub_diskfilter_vg *array; char *uuid; + grub_uint64_t sb_sz, data_end, sb_end; if (size == GRUB_DISK_SIZE_UNKNOWN && minor_version == 0) continue; @@ -154,6 +159,79 @@ grub_mdraid_detect (grub_disk_t disk, || grub_le_to_cpu64 (sb.super_offset) != sector) continue; + /* + * The first check follows the Linux kernel's data_size + * validation from v6.8-rc5. + */ + if (grub_le_to_cpu64 (sb.data_size) < 10 || + grub_le_to_cpu64 (sb.raid_disks) > GRUB_MDRAID_MAX_DISKS) + { + grub_dprintf ("mdraid1x", "Corrupted superblock\n"); + return NULL; + } + + /* + * Total size of superblock: 256 bytes plus 2 bytes per device + * in the array. + */ + sb_sz = sizeof (struct grub_raid_super_1x) + grub_le_to_cpu64 (sb.raid_disks) * 2; + + if (grub_add (grub_le_to_cpu64 (sb.super_offset), + (ALIGN_UP(sb_sz, GRUB_MD_SECTOR_SIZE) >> GRUB_MD_SECTOR_SHIFT), &sb_end)) + { + grub_dprintf ("mdraid1x", "Invalid superblock end.\n"); + return NULL; + } + + if (grub_add (grub_le_to_cpu64 (sb.data_offset), + grub_le_to_cpu64 (sb.data_size), &data_end)) + { + grub_dprintf ("mdraid1x", "Invalid data end.\n"); + return NULL; + } + + /* In minor versions 1 and 2, superblock is positioned before data. */ + if (minor_version) + { + if (grub_le_to_cpu64 (sb.data_offset) < sb_end) + { + grub_dprintf ("mdraid1x", + "The superblock either overlaps with the data " + "or is behind it.\n"); + return NULL; + } + + if (data_end > size) + { + grub_dprintf ("mdraid1x", + "The data region ends at %" PRIuGRUB_UINT64_T ", " + "past the end of the disk (%" PRIuGRUB_UINT64_T ")\n", + data_end, size); + return NULL; + } + } + else + { + /* In minor version 0, superblock is at the end of the device. */ + if (grub_le_to_cpu64 (sb.super_offset) < data_end) + { + grub_dprintf ("mdraid1x", + "The data either overlaps with the superblock " + "or is behind it.\n"); + return NULL; + } + + if (sb_end > size) + { + grub_dprintf ("mdraid1x", + "The superblock region ends at " + "%" PRIuGRUB_UINT64_T ", past the end of " + "the disk (%" PRIuGRUB_UINT64_T ")\n", + sb_end, size); + return NULL; + } + } + if (sb.major_version != grub_cpu_to_le32_compile_time (1)) /* Unsupported version. */ return NULL; From 386b59ddb42fa3f86ddfe557113b25c8fa16f88c Mon Sep 17 00:00:00 2001 From: Forest Date: Mon, 6 May 2024 17:07:30 -0700 Subject: [PATCH 131/402] disk/cryptodisk: Allow user to retry failed passphrase Give the user a chance to re-enter their cryptodisk passphrase after a typo, rather than immediately failing (and likely dumping them into a GRUB shell). By default, we allow 3 tries before giving up. A value in the cryptodisk_passphrase_tries environment variable will override this default. The user can give up early by entering an empty passphrase, just as they could before this patch. Signed-off-by: Forest Reviewed-by: Daniel Kiper --- docs/grub.texi | 9 +++++ grub-core/disk/cryptodisk.c | 73 ++++++++++++++++++++++++++++--------- 2 files changed, 65 insertions(+), 17 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index a225f9a88..d32266f69 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -3277,6 +3277,7 @@ These variables have special meaning to GRUB. * color_normal:: * config_directory:: * config_file:: +* cryptodisk_passphrase_tries:: * debug:: * default:: * fallback:: @@ -3441,6 +3442,14 @@ processed by commands @command{configfile} (@pxref{configfile}) or @command{norm (@pxref{normal}). It is restored to the previous value when command completes. +@node cryptodisk_passphrase_tries +@subsection cryptodisk_passphrase_tries + +When prompting the user for a cryptodisk passphrase, allow this many attempts +before giving up. Defaults to @samp{3} if unset or set to an invalid value. +(The user can give up early by entering an empty passphrase.) + + @node debug @subsection debug diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index 2246af51b..f98acade6 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -17,6 +17,7 @@ */ #include +#include #include #include #include @@ -1114,32 +1115,70 @@ grub_cryptodisk_scan_device_real (const char *name, if (!dev) continue; - if (!cargs->key_len) + if (cargs->key_len) + { + ret = cr->recover_key (source, dev, cargs); + if (ret != GRUB_ERR_NONE) + goto error; + } + else { /* Get the passphrase from the user, if no key data. */ - askpass = 1; - part = grub_partition_get_name (source->partition); - grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "), source->name, - source->partition != NULL ? "," : "", - part != NULL ? part : N_("UNKNOWN"), - dev->uuid); - grub_free (part); + unsigned long tries = 3; + const char *tries_env; + askpass = 1; cargs->key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE); if (cargs->key_data == NULL) goto error_no_close; - if (!grub_password_get ((char *) cargs->key_data, GRUB_CRYPTODISK_MAX_PASSPHRASE)) + tries_env = grub_env_get ("cryptodisk_passphrase_tries"); + if (tries_env != NULL && tries_env[0] != '\0') { - grub_error (GRUB_ERR_BAD_ARGUMENT, "passphrase not supplied"); - goto error; - } - cargs->key_len = grub_strlen ((char *) cargs->key_data); - } + unsigned long tries_env_val; + const char *p; - ret = cr->recover_key (source, dev, cargs); - if (ret != GRUB_ERR_NONE) - goto error; + tries_env_val = grub_strtoul (tries_env, &p, 0); + if (*p == '\0' && tries_env_val != ~0UL) + tries = tries_env_val; + else + grub_printf_ (N_("Invalid cryptodisk_passphrase_tries value `%s'. Defaulting to %lu.\n"), + tries_env, + tries); + } + + for (; tries > 0; tries--) + { + part = grub_partition_get_name (source->partition); + grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "), source->name, + source->partition != NULL ? "," : "", + part != NULL ? part : N_("UNKNOWN"), + dev->uuid); + grub_free (part); + + if (!grub_password_get ((char *) cargs->key_data, GRUB_CRYPTODISK_MAX_PASSPHRASE)) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "passphrase not supplied"); + goto error; + } + cargs->key_len = grub_strlen ((char *) cargs->key_data); + + ret = cr->recover_key (source, dev, cargs); + if (ret == GRUB_ERR_NONE) + break; + if (ret != GRUB_ERR_ACCESS_DENIED || tries == 1) + goto error; + grub_puts_ (N_("Invalid passphrase.")); + + /* + * Since recover_key() calls a function that returns grub_errno, + * a leftover error value from a previously rejected passphrase + * will trigger a phantom failure. We therefore clear it before + * trying a new passphrase. + */ + grub_errno = GRUB_ERR_NONE; + } + } ret = grub_cryptodisk_insert (dev, name, source); if (ret != GRUB_ERR_NONE) From f456add5f4171b27a1f8aca98e02c14e123bbd59 Mon Sep 17 00:00:00 2001 From: Rogier Date: Mon, 6 May 2024 15:18:45 -0500 Subject: [PATCH 132/402] disk/lvm: GRUB fails to detect LVM volumes due to an incorrect computation of mda_end When handling a regular LVM volume, GRUB can fail with the message: error: disk `lvmid/******-****-****-****-****-****-****/******-****-****-****-****-****-******' not found. If the condition which triggers this exists, grub-probe will report the error mentioned above. Similarly, the GRUB boot code will fail to detect LVM volumes, resulting in a failure to boot off of LVM disks/partitions. The condition can be created on any LVM VG by an LVM configuration change, so any system with /boot on LVM can become unbootable at "any" time (after any LVM configuration change). The problem is caused by an incorrect computation of mda_end in disk/lvm.c, when the metadata area wraps around. Apparently, this can start happening at around 220 metadata changes to the VG. Fixes: 879c4a834 (lvm: Fix two more potential data-dependent alloc overflows) Fixes: https://savannah.gnu.org/bugs/?61620 Signed-off-by: Rogier Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper Tested-By: Michael Chang --- grub-core/disk/lvm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/disk/lvm.c b/grub-core/disk/lvm.c index 794248540..0c32c95f9 100644 --- a/grub-core/disk/lvm.c +++ b/grub-core/disk/lvm.c @@ -290,7 +290,7 @@ grub_lvm_detect (grub_disk_t disk, p = q = (char *)ptr; - if (grub_add ((grub_size_t)metadatabuf, (grub_size_t)mda_size, &ptr)) + if (grub_add (ptr, (grub_size_t) grub_le_to_cpu64 (rlocn->size), &ptr)) goto error_parsing_metadata; mda_end = (char *)ptr; From 6cc2e4481b5051febf50390839a9c1707e877731 Mon Sep 17 00:00:00 2001 From: Pascal Hambourg Date: Sun, 19 May 2024 17:50:10 +0200 Subject: [PATCH 133/402] util/grub.d/00_header.in: Quote background image pathname in output This is required if the pathname contains spaces or GRUB shell metacharacters else the generated config file check will fail. Signed-off-by: Pascal Hambourg Reviewed-by: Daniel Kiper --- util/grub.d/00_header.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in index 6a316a5ba..f86b69bad 100644 --- a/util/grub.d/00_header.in +++ b/util/grub.d/00_header.in @@ -275,7 +275,7 @@ EOF prepare_grub_to_access_device `${grub_probe} --target=device "$GRUB_BACKGROUND"` cat << EOF insmod $reader -background_image -m stretch `make_system_path_relative_to_its_root "$GRUB_BACKGROUND"` +background_image -m stretch "`make_system_path_relative_to_its_root "$GRUB_BACKGROUND"`" EOF fi fi From d291449ba3a7b51c4469404c2036a91002788d69 Mon Sep 17 00:00:00 2001 From: Jonathan Davies Date: Mon, 20 May 2024 08:58:24 +0100 Subject: [PATCH 134/402] docs: Fix spelling mistakes Signed-off-by: Jonathan Davies Reviewed-by: Daniel Kiper --- docs/grub.texi | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index d32266f69..1f6d0c724 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -1259,7 +1259,7 @@ need to write the whole thing by hand. @menu * Simple configuration:: Recommended for most users -* Root Identifcation Heuristics:: Summary on how the root file system is identified. +* Root Identification Heuristics:: Summary on how the root file system is identified. * Shell-like scripting:: For power users and developers * Multi-boot manual config:: For non-standard multi-OS scenarios * Embedded configuration:: Embedding a configuration file into GRUB @@ -1509,7 +1509,7 @@ search for files. This is usually more reliable, but in some cases it may not be appropriate. To disable this use of UUIDs, set this option to @samp{true}. Setting this option to @samp{true}, will also set the options @samp{GRUB_DISABLE_LINUX_UUID} and @samp{GRUB_DISABLE_LINUX_PARTUUID} to -@samp{true}, unless they have been explicilty set to @samp{false}. +@samp{true}, unless they have been explicitly set to @samp{false}. @item GRUB_VIDEO_BACKEND If graphical video support is required, either because the @samp{gfxterm} @@ -1642,8 +1642,8 @@ edit the scripts in @file{/etc/grub.d} directly. menu entries; simply type the menu entries you want to add at the end of that file, making sure to leave at least the first two lines intact. -@node Root Identifcation Heuristics -@section Root Identifcation Heuristics +@node Root Identification Heuristics +@section Root Identification Heuristics If the target operating system uses the Linux kernel, @command{grub-mkconfig} attempts to identify the root file system via a heuristic algoirthm. This algorithm selects the identification method of the root file system by @@ -2577,7 +2577,7 @@ value corresponds to a value on the client machine. @end example The UUID is the Client Machine Identifier Option Definition as specified in -RFC 4578. The client will only attempt to loouk up a UUID config file if it +RFC 4578. The client will only attempt to look up a UUID config file if it was provided by the DHCP server. The client will only attempt to look up an IPv6 address config once, however, @@ -2986,12 +2986,12 @@ The device syntax is like this: driver in use. BIOS and EFI disks use either @samp{fd} or @samp{hd} followed by a digit, like @samp{fd0}, or @samp{cd}. AHCI, PATA (ata), crypto, USB use the name of driver followed by a number. -Memdisk and host are limited to one disk and so it's refered just by driver +Memdisk and host are limited to one disk and so it's referred just by driver name. RAID (md), ofdisk (ieee1275 and nand), LVM (lvm), LDM, virtio (vdsk) and arcdisk (arc) use intrinsic name of disk prefixed by driver name. Additionally just ``nand'' refers to the disk aliased as ``nand''. -Conflicts are solved by suffixing a number if necessarry. +Conflicts are solved by suffixing a number if necessary. Commas need to be escaped. Loopback uses whatever name specified to @command{loopback} command. Hostdisk uses names specified in device.map as long as it's of the form @@ -4690,7 +4690,7 @@ However, this limitation can be worked around by using blocklist syntax. So for instance, @code{(hd1,gpt2)} can not be used, but @code{(hd1,gpt2)0+} will achieve the desired result. -GRUB suports devices encrypted using LUKS, LUKS2 and geli. Note that necessary +GRUB supports devices encrypted using LUKS, LUKS2 and geli. Note that necessary modules (@var{luks}, @var{luks2} and @var{geli}) have to be loaded manually before this command can be used. For LUKS2 only the PBKDF2 key derivation function is supported, as Argon2 is not yet supported. @@ -6354,7 +6354,7 @@ Moreover all current input consumers are limited to ASCII. GRUB supports being translated. For this you need to have language *.mo files in $prefix/locale, load gettext module and set ``lang'' variable. @section Regexp -Regexps work on unicode characters, however no attempt at checking cannonical +Regexps work on unicode characters, however no attempt at checking canonical equivalence has been made. Moreover the classes like [:alpha:] match only ASCII subset. @@ -6368,7 +6368,7 @@ matched as binary. Similar behaviour is for matching OSBundleRequired. Since IEEE1275 aliases and OSBundleRequired don't contain any non-ASCII it should never be a problem in practice. Case-sensitive identifiers are matched as raw strings, no canonical -equivalence check is performed. Case-insenstive identifiers are matched +equivalence check is performed. Case-insensitive identifiers are matched as RAW but additionally [a-z] is equivalent to [A-Z]. GRUB-defined identifiers use only ASCII and so should user-defined ones. Identifiers containing non-ASCII may work but aren't supported. @@ -6656,7 +6656,7 @@ On EMU platform no serial port is available. Console charset refers only to firmware-assisted console. gfxterm is always Unicode (see Internationalisation section for its limitations). Serial is configurable to UTF-8 or ASCII (see Internationalisation). In case of qemu -and coreboot ports the refered console is vga_text. Loongson always uses +and coreboot ports the referred console is vga_text. Loongson always uses gfxterm. Most limited one is ASCII. CP437 provides additionally pseudographics. @@ -6818,7 +6818,7 @@ Advanced operations for power users: @item x86: iorw (direct access to I/O ports) @end itemize -Miscelaneous: +Miscellaneous: @itemize @item cmos (x86-*, ieee1275, mips-qemu_mips, mips-loongson): cmostest (used on some laptops to check for special power-on key), cmosclean From 1ba39de62f91302f7eb95e78dc8ce9c086f74df8 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Tue, 21 May 2024 01:20:57 +0800 Subject: [PATCH 135/402] safemath: Add ALIGN_UP_OVF() which checks for an overflow The following EROFS patch will use this helper to handle ALIGN_UP() overflow. Signed-off-by: Gao Xiang Reviewed-by: Daniel Kiper --- include/grub/safemath.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/include/grub/safemath.h b/include/grub/safemath.h index fbd9b5925..e032f63a0 100644 --- a/include/grub/safemath.h +++ b/include/grub/safemath.h @@ -32,6 +32,21 @@ #define grub_cast(a, res) grub_add ((a), 0, (res)) +/* + * It's caller's responsibility to check "align" does not equal 0 and + * is power of 2. + */ +#define ALIGN_UP_OVF(v, align, res) \ +({ \ + bool __failed; \ + typeof(v) __a = ((typeof(v))(align) - 1); \ + \ + __failed = grub_add (v, __a, res); \ + if (__failed == false) \ + *(res) &= ~__a; \ + __failed; \ +}) + #else #error gcc 5.1 or newer or clang 8.0 or newer is required #endif From 9d603061aaa5edb9a0e5ba443ec01d5c295bef0a Mon Sep 17 00:00:00 2001 From: Yifan Zhao Date: Tue, 21 May 2024 01:20:58 +0800 Subject: [PATCH 136/402] fs/erofs: Add support for the EROFS The EROFS [1] is a lightweight read-only filesystem designed for performance which has already been shipped in most Linux distributions as well as widely used in several scenarios, such as Android system partitions, container images and rootfs for embedded devices. This patch brings in the EROFS uncompressed support. Now, it's possible to boot directly through GRUB with an EROFS rootfs. Support for the EROFS compressed files will be added later. [1] https://erofs.docs.kernel.org Signed-off-by: Yifan Zhao Tested-by: Daniel Axtens Signed-off-by: Gao Xiang Reviewed-by: Daniel Kiper --- INSTALL | 8 +- Makefile.util.def | 1 + docs/grub.texi | 3 +- grub-core/Makefile.core.def | 5 + grub-core/fs/erofs.c | 1000 +++++++++++++++++++++++++++++++++++ 5 files changed, 1012 insertions(+), 5 deletions(-) create mode 100644 grub-core/fs/erofs.c diff --git a/INSTALL b/INSTALL index 8d9207c84..84030c9f4 100644 --- a/INSTALL +++ b/INSTALL @@ -77,15 +77,15 @@ Prerequisites for make-check: * If running a Linux kernel the following modules must be loaded: - fuse, loop - - btrfs, ext4, f2fs, fat, hfs, hfsplus, jfs, mac-roman, minix, nilfs2, + - btrfs, erofs, ext4, f2fs, fat, hfs, hfsplus, jfs, mac-roman, minix, nilfs2, reiserfs, udf, xfs - On newer kernels, the exfat kernel modules may be used instead of the exfat FUSE filesystem * 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, exfat-utils, f2fs-tools, genromfs, - hfsprogs, jfsutils, nilfs-tools, ntfs-3g, reiserfsprogs, squashfs-tools, - reiserfsprogs, udftools, xfsprogs, zfs-fuse + - btrfs-progs, dosfstools, e2fsprogs, erofs-utils, exfat-utils, f2fs-tools, + genromfs, hfsprogs, jfsutils, nilfs-tools, ntfs-3g, reiserfsprogs, + squashfs-tools, reiserfsprogs, udftools, xfsprogs, zfs-fuse - exfat-fuse, if not using the exfat kernel module - gzip, lzop, xz-utils - attr, cpio, g++, gawk, parted, recode, tar, util-linux diff --git a/Makefile.util.def b/Makefile.util.def index 9432365a9..8d3bc107f 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -98,6 +98,7 @@ library = { common = grub-core/fs/cpio_be.c; common = grub-core/fs/odc.c; common = grub-core/fs/newc.c; + common = grub-core/fs/erofs.c; common = grub-core/fs/ext2.c; common = grub-core/fs/fat.c; common = grub-core/fs/exfat.c; diff --git a/docs/grub.texi b/docs/grub.texi index 1f6d0c724..cb3a14fca 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -353,6 +353,7 @@ blocklist notation. The currently supported filesystem types are @dfn{Amiga Fast FileSystem (AFFS)}, @dfn{AtheOS fs}, @dfn{BeFS}, @dfn{BtrFS} (including raid0, raid1, raid10, gzip and lzo), @dfn{cpio} (little- and big-endian bin, odc and newc variants), +@dfn{EROFS} (only uncompressed support for now), @dfn{Linux ext2/ext3/ext4}, @dfn{DOS FAT12/FAT16/FAT32}, @dfn{exFAT}, @dfn{F2FS}, @dfn{HFS}, @dfn{HFS+}, @dfn{ISO9660} (including Joliet, Rock-ridge and multi-chunk files), @@ -6276,7 +6277,7 @@ assumed to be encoded in UTF-8. NTFS, JFS, UDF, HFS+, exFAT, long filenames in FAT, Joliet part of ISO9660 are treated as UTF-16 as per specification. AFS and BFS are read as UTF-8, again according to specification. BtrFS, cpio, tar, squash4, minix, -minix2, minix3, ROMFS, ReiserFS, XFS, ext2, ext3, ext4, FAT (short names), +minix2, minix3, ROMFS, ReiserFS, XFS, EROFS, ext2, ext3, ext4, FAT (short names), F2FS, RockRidge part of ISO9660, nilfs2, UFS1, UFS2 and ZFS are assumed to be UTF-8. This might be false on systems configured with legacy charset but as long as the charset used is superset of ASCII you should be able to diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 8e1b1d9f3..7fa9446bd 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1442,6 +1442,11 @@ module = { common = fs/odc.c; }; +module = { + name = erofs; + common = fs/erofs.c; +}; + module = { name = ext2; common = fs/ext2.c; diff --git a/grub-core/fs/erofs.c b/grub-core/fs/erofs.c new file mode 100644 index 000000000..46cfc2e5c --- /dev/null +++ b/grub-core/fs/erofs.c @@ -0,0 +1,1000 @@ +/* erofs.c - Enhanced Read-Only File System */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2024 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 + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define EROFS_SUPER_OFFSET 1024 +#define EROFS_MAGIC 0xE0F5E1E2 +#define EROFS_ISLOTBITS 5 + +#define EROFS_FEATURE_INCOMPAT_CHUNKED_FILE 0x00000004 +#define EROFS_ALL_FEATURE_INCOMPAT EROFS_FEATURE_INCOMPAT_CHUNKED_FILE + +struct grub_erofs_super +{ + grub_uint32_t magic; + grub_uint32_t checksum; + grub_uint32_t feature_compat; + grub_uint8_t log2_blksz; + grub_uint8_t sb_extslots; + + grub_uint16_t root_nid; + grub_uint64_t inos; + + grub_uint64_t build_time; + grub_uint32_t build_time_nsec; + grub_uint32_t blocks; + grub_uint32_t meta_blkaddr; + grub_uint32_t xattr_blkaddr; + grub_packed_guid_t uuid; + grub_uint8_t volume_name[16]; + grub_uint32_t feature_incompat; + + union + { + grub_uint16_t available_compr_algs; + grub_uint16_t lz4_max_distance; + } GRUB_PACKED u1; + + grub_uint16_t extra_devices; + grub_uint16_t devt_slotoff; + grub_uint8_t log2_dirblksz; + grub_uint8_t xattr_prefix_count; + grub_uint32_t xattr_prefix_start; + grub_uint64_t packed_nid; + grub_uint8_t reserved2[24]; +} GRUB_PACKED; + +#define EROFS_INODE_LAYOUT_COMPACT 0 +#define EROFS_INODE_LAYOUT_EXTENDED 1 + +#define EROFS_INODE_FLAT_PLAIN 0 +#define EROFS_INODE_COMPRESSED_FULL 1 +#define EROFS_INODE_FLAT_INLINE 2 +#define EROFS_INODE_COMPRESSED_COMPACT 3 +#define EROFS_INODE_CHUNK_BASED 4 + +#define EROFS_I_VERSION_MASKS 0x01 +#define EROFS_I_DATALAYOUT_MASKS 0x07 + +#define EROFS_I_VERSION_BIT 0 +#define EROFS_I_DATALAYOUT_BIT 1 + +struct grub_erofs_inode_chunk_info +{ + grub_uint16_t format; + grub_uint16_t reserved; +} GRUB_PACKED; + +#define EROFS_CHUNK_FORMAT_BLKBITS_MASK 0x001F +#define EROFS_CHUNK_FORMAT_INDEXES 0x0020 + +#define EROFS_BLOCK_MAP_ENTRY_SIZE 4 +#define EROFS_MAP_MAPPED 0x02 + +#define EROFS_NULL_ADDR 1 +#define EROFS_NAME_LEN 255 +#define EROFS_PATH_LEN 4096 +#define EROFS_MIN_LOG2_BLOCK_SIZE 9 +#define EROFS_MAX_LOG2_BLOCK_SIZE 16 + +struct grub_erofs_inode_chunk_index +{ + grub_uint16_t advise; + grub_uint16_t device_id; + grub_uint32_t blkaddr; +}; + +union grub_erofs_inode_i_u +{ + grub_uint32_t compressed_blocks; + grub_uint32_t raw_blkaddr; + + grub_uint32_t rdev; + + struct grub_erofs_inode_chunk_info c; +}; + +struct grub_erofs_inode_compact +{ + grub_uint16_t i_format; + + grub_uint16_t i_xattr_icount; + grub_uint16_t i_mode; + grub_uint16_t i_nlink; + grub_uint32_t i_size; + grub_uint32_t i_reserved; + + union grub_erofs_inode_i_u i_u; + + grub_uint32_t i_ino; + grub_uint16_t i_uid; + grub_uint16_t i_gid; + grub_uint32_t i_reserved2; +} GRUB_PACKED; + +struct grub_erofs_inode_extended +{ + grub_uint16_t i_format; + + grub_uint16_t i_xattr_icount; + grub_uint16_t i_mode; + grub_uint16_t i_reserved; + grub_uint64_t i_size; + + union grub_erofs_inode_i_u i_u; + + grub_uint32_t i_ino; + + grub_uint32_t i_uid; + grub_uint32_t i_gid; + grub_uint64_t i_mtime; + grub_uint32_t i_mtime_nsec; + grub_uint32_t i_nlink; + grub_uint8_t i_reserved2[16]; +} GRUB_PACKED; + +union grub_erofs_inode +{ + struct grub_erofs_inode_compact c; + struct grub_erofs_inode_extended e; +} GRUB_PACKED; + +#define EROFS_FT_UNKNOWN 0 +#define EROFS_FT_REG_FILE 1 +#define EROFS_FT_DIR 2 +#define EROFS_FT_CHRDEV 3 +#define EROFS_FT_BLKDEV 4 +#define EROFS_FT_FIFO 5 +#define EROFS_FT_SOCK 6 +#define EROFS_FT_SYMLINK 7 + +struct grub_erofs_dirent +{ + grub_uint64_t nid; + grub_uint16_t nameoff; + grub_uint8_t file_type; + grub_uint8_t reserved; +} GRUB_PACKED; + +struct grub_erofs_map_blocks +{ + grub_uint64_t m_pa; /* physical address */ + grub_uint64_t m_la; /* logical address */ + grub_uint64_t m_plen; /* physical length */ + grub_uint64_t m_llen; /* logical length */ + grub_uint32_t m_flags; +}; + +struct grub_erofs_xattr_ibody_header +{ + grub_uint32_t h_reserved; + grub_uint8_t h_shared_count; + grub_uint8_t h_reserved2[7]; + grub_uint32_t h_shared_xattrs[0]; +}; + +struct grub_fshelp_node +{ + struct grub_erofs_data *data; + union grub_erofs_inode inode; + + grub_uint64_t ino; + grub_uint8_t inode_type; + grub_uint8_t inode_datalayout; + + /* If the inode has been read into memory? */ + bool inode_loaded; +}; + +struct grub_erofs_data +{ + grub_disk_t disk; + struct grub_erofs_super sb; + + struct grub_fshelp_node inode; +}; + +#define erofs_blocksz(data) (((grub_uint32_t) 1) << data->sb.log2_blksz) + +static grub_size_t +grub_erofs_strnlen (const char *s, grub_size_t n) +{ + const char *p = s; + + if (n == 0) + return 0; + + while (n-- && *p) + p++; + + return p - s; +} + +static grub_uint64_t +erofs_iloc (grub_fshelp_node_t node) +{ + struct grub_erofs_super *sb = &node->data->sb; + + return ((grub_uint64_t) grub_le_to_cpu32 (sb->meta_blkaddr) << sb->log2_blksz) + + (node->ino << EROFS_ISLOTBITS); +} + +static grub_err_t +erofs_read_inode (struct grub_erofs_data *data, grub_fshelp_node_t node) +{ + union grub_erofs_inode *di; + grub_err_t err; + grub_uint16_t i_format; + grub_uint64_t addr = erofs_iloc (node); + + di = (union grub_erofs_inode *) &node->inode; + + err = grub_disk_read (data->disk, addr >> GRUB_DISK_SECTOR_BITS, + addr & (GRUB_DISK_SECTOR_SIZE - 1), + sizeof (struct grub_erofs_inode_compact), &di->c); + if (err != GRUB_ERR_NONE) + return err; + + i_format = grub_le_to_cpu16 (di->c.i_format); + node->inode_type = (i_format >> EROFS_I_VERSION_BIT) & EROFS_I_VERSION_MASKS; + node->inode_datalayout = (i_format >> EROFS_I_DATALAYOUT_BIT) & EROFS_I_DATALAYOUT_MASKS; + + switch (node->inode_type) + { + case EROFS_INODE_LAYOUT_EXTENDED: + addr += sizeof (struct grub_erofs_inode_compact); + err = grub_disk_read (data->disk, addr >> GRUB_DISK_SECTOR_BITS, + addr & (GRUB_DISK_SECTOR_SIZE - 1), + sizeof (struct grub_erofs_inode_extended) - sizeof (struct grub_erofs_inode_compact), + (grub_uint8_t *) di + sizeof (struct grub_erofs_inode_compact)); + if (err != GRUB_ERR_NONE) + return err; + break; + case EROFS_INODE_LAYOUT_COMPACT: + break; + default: + return grub_error (GRUB_ERR_BAD_FS, "invalid type %u @ inode %" PRIuGRUB_UINT64_T, + node->inode_type, node->ino); + } + + node->inode_loaded = true; + + return 0; +} + +static grub_uint64_t +erofs_inode_size (grub_fshelp_node_t node) +{ + return node->inode_type == EROFS_INODE_LAYOUT_COMPACT + ? sizeof (struct grub_erofs_inode_compact) + : sizeof (struct grub_erofs_inode_extended); +} + +static grub_uint64_t +erofs_inode_file_size (grub_fshelp_node_t node) +{ + union grub_erofs_inode *di = (union grub_erofs_inode *) &node->inode; + + return node->inode_type == EROFS_INODE_LAYOUT_COMPACT + ? grub_le_to_cpu32 (di->c.i_size) + : grub_le_to_cpu64 (di->e.i_size); +} + +static grub_uint32_t +erofs_inode_xattr_ibody_size (grub_fshelp_node_t node) +{ + grub_uint16_t cnt = grub_le_to_cpu16 (node->inode.e.i_xattr_icount); + + if (cnt == 0) + return 0; + + return sizeof (struct grub_erofs_xattr_ibody_header) + ((cnt - 1) * sizeof (grub_uint32_t)); +} + +static grub_uint64_t +erofs_inode_mtime (grub_fshelp_node_t node) +{ + return node->inode_type == EROFS_INODE_LAYOUT_COMPACT + ? grub_le_to_cpu64 (node->data->sb.build_time) + : grub_le_to_cpu64 (node->inode.e.i_mtime); +} + +static grub_err_t +erofs_map_blocks_flatmode (grub_fshelp_node_t node, + struct grub_erofs_map_blocks *map) +{ + grub_uint64_t nblocks, lastblk, file_size; + bool tailendpacking = (node->inode_datalayout == EROFS_INODE_FLAT_INLINE); + grub_uint64_t blocksz = erofs_blocksz (node->data); + + /* `file_size` is checked by caller and cannot be zero, hence nblocks > 0. */ + file_size = erofs_inode_file_size (node); + if (grub_add (file_size, blocksz - 1, &nblocks)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "nblocks overflow"); + nblocks >>= node->data->sb.log2_blksz; + lastblk = nblocks - tailendpacking; + + map->m_flags = EROFS_MAP_MAPPED; + + /* No overflow as (lastblk <= nblocks) && (nblocks * blocksz <= UINT64_MAX - blocksz + 1). */ + if (map->m_la < (lastblk * blocksz)) + { + if (grub_mul ((grub_uint64_t) grub_le_to_cpu32 (node->inode.e.i_u.raw_blkaddr), blocksz, &map->m_pa) || + grub_add (map->m_pa, map->m_la, &map->m_pa)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "m_pa overflow"); + if (grub_sub (lastblk * blocksz, map->m_la, &map->m_plen)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "m_plen underflow"); + } + else if (tailendpacking) + { + if (grub_add (erofs_iloc (node), erofs_inode_size (node), &map->m_pa) || + grub_add (map->m_pa, erofs_inode_xattr_ibody_size (node), &map->m_pa) || + grub_add (map->m_pa, map->m_la % blocksz, &map->m_pa)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "m_pa overflow when handling tailpacking"); + if (grub_sub (file_size, map->m_la, &map->m_plen)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "m_plen overflow when handling tailpacking"); + + /* No overflow as map->m_plen <= UINT64_MAX - blocksz + 1. */ + if (((map->m_pa % blocksz) + map->m_plen) > blocksz) + return grub_error (GRUB_ERR_BAD_FS, + "inline data cross block boundary @ inode %" PRIuGRUB_UINT64_T, + node->ino); + } + else + return grub_error (GRUB_ERR_BAD_FS, + "invalid map->m_la=%" PRIuGRUB_UINT64_T + " @ inode %" PRIuGRUB_UINT64_T, + map->m_la, node->ino); + + map->m_llen = map->m_plen; + return GRUB_ERR_NONE; +} + +static grub_err_t +erofs_map_blocks_chunkmode (grub_fshelp_node_t node, + struct grub_erofs_map_blocks *map) +{ + grub_uint16_t chunk_format = grub_le_to_cpu16 (node->inode.e.i_u.c.format); + grub_uint64_t unit, pos, chunknr, blkaddr; + grub_uint8_t chunkbits; + grub_err_t err; + + if (chunk_format & EROFS_CHUNK_FORMAT_INDEXES) + unit = sizeof (struct grub_erofs_inode_chunk_index); + else + unit = EROFS_BLOCK_MAP_ENTRY_SIZE; + + chunkbits = node->data->sb.log2_blksz + (chunk_format & EROFS_CHUNK_FORMAT_BLKBITS_MASK); + if (chunkbits > 63) + return grub_error (GRUB_ERR_BAD_FS, "invalid chunkbits %u @ inode %" PRIuGRUB_UINT64_T, + chunkbits, node->ino); + + chunknr = map->m_la >> chunkbits; + + if (grub_add (erofs_iloc (node), erofs_inode_size (node), &pos)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "chunkmap position overflow when adding inode size"); + + if (grub_add (pos, erofs_inode_xattr_ibody_size (node), &pos)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "chunkmap position overflow when adding xattr size"); + + if (ALIGN_UP_OVF (pos, unit, &pos)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "position overflow when seeking at the start of chunkmap"); + + /* No overflow for multiplication as chunkbits >= 9 and sizeof(unit) <= 8. */ + if (grub_add (pos, chunknr * unit, &pos)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "chunkmap position overflow when finding the specific chunk"); + + map->m_la = chunknr << chunkbits; + + if (grub_sub (erofs_inode_file_size (node), map->m_la, &map->m_plen)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "m_plen underflow"); + map->m_plen = grub_min (((grub_uint64_t) 1) << chunkbits, + ALIGN_UP (map->m_plen, erofs_blocksz (node->data))); + + if (chunk_format & EROFS_CHUNK_FORMAT_INDEXES) + { + struct grub_erofs_inode_chunk_index idx; + + err = grub_disk_read (node->data->disk, pos >> GRUB_DISK_SECTOR_BITS, + pos & (GRUB_DISK_SECTOR_SIZE - 1), unit, &idx); + if (err != GRUB_ERR_NONE) + return err; + + blkaddr = grub_le_to_cpu32 (idx.blkaddr); + } + else + { + grub_uint32_t blkaddr_le; + + err = grub_disk_read (node->data->disk, pos >> GRUB_DISK_SECTOR_BITS, + pos & (GRUB_DISK_SECTOR_SIZE - 1), unit, &blkaddr_le); + if (err != GRUB_ERR_NONE) + return err; + + blkaddr = grub_le_to_cpu32 (blkaddr_le); + } + + if (blkaddr == EROFS_NULL_ADDR) + { + map->m_pa = 0; + map->m_flags = 0; + } + else + { + map->m_pa = blkaddr << node->data->sb.log2_blksz; + map->m_flags = EROFS_MAP_MAPPED; + } + + map->m_llen = map->m_plen; + return GRUB_ERR_NONE; +} + +static grub_err_t +erofs_map_blocks (grub_fshelp_node_t node, struct grub_erofs_map_blocks *map) +{ + if (map->m_la >= erofs_inode_file_size (node)) + { + map->m_llen = map->m_plen = 0; + map->m_pa = 0; + map->m_flags = 0; + return GRUB_ERR_NONE; + } + + if (node->inode_datalayout != EROFS_INODE_CHUNK_BASED) + return erofs_map_blocks_flatmode (node, map); + else + return erofs_map_blocks_chunkmode (node, map); +} + +static grub_err_t +erofs_read_raw_data (grub_fshelp_node_t node, grub_uint8_t *buf, grub_uint64_t size, + grub_uint64_t offset, grub_uint64_t *bytes) +{ + struct grub_erofs_map_blocks map = {0}; + grub_uint64_t cur; + grub_err_t err; + + if (bytes) + *bytes = 0; + + if (node->inode_loaded == false) + { + err = erofs_read_inode (node->data, node); + if (err != GRUB_ERR_NONE) + return err; + } + + cur = offset; + while (cur < offset + size) + { + grub_uint8_t *const estart = buf + cur - offset; + grub_uint64_t eend, moff = 0; + + map.m_la = cur; + err = erofs_map_blocks (node, &map); + if (err != GRUB_ERR_NONE) + return err; + + if (grub_add(map.m_la, map.m_llen, &eend)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "eend overflow"); + + eend = grub_min (eend, offset + size); + if (!(map.m_flags & EROFS_MAP_MAPPED)) + { + if (!map.m_llen) + { + /* Reached EOF. */ + grub_memset (estart, 0, offset + size - cur); + cur = offset + size; + continue; + } + + /* It's a hole. */ + grub_memset (estart, 0, eend - cur); + if (bytes) + *bytes += eend - cur; + cur = eend; + continue; + } + + if (cur > map.m_la) + { + moff = cur - map.m_la; + map.m_la = cur; + } + + err = grub_disk_read (node->data->disk, + (map.m_pa + moff) >> GRUB_DISK_SECTOR_BITS, + (map.m_pa + moff) & (GRUB_DISK_SECTOR_SIZE - 1), + eend - map.m_la, estart); + if (err != GRUB_ERR_NONE) + return err; + + if (bytes) + *bytes += eend - map.m_la; + + cur = eend; + } + + return GRUB_ERR_NONE; +} + +static int +erofs_iterate_dir (grub_fshelp_node_t dir, grub_fshelp_iterate_dir_hook_t hook, + void *hook_data) +{ + grub_uint64_t offset = 0, file_size; + grub_uint32_t blocksz = erofs_blocksz (dir->data); + grub_uint8_t *buf; + grub_err_t err; + + if (dir->inode_loaded == false) + { + err = erofs_read_inode (dir->data, dir); + if (err != GRUB_ERR_NONE) + return 0; + } + + file_size = erofs_inode_file_size (dir); + buf = grub_malloc (blocksz); + if (buf == NULL) + return 0; + + while (offset < file_size) + { + grub_uint64_t maxsize = grub_min (blocksz, file_size - offset); + struct grub_erofs_dirent *de = (void *) buf, *end; + grub_uint16_t nameoff; + + err = erofs_read_raw_data (dir, buf, maxsize, offset, NULL); + if (err != GRUB_ERR_NONE) + goto not_found; + + nameoff = grub_le_to_cpu16 (de->nameoff); + if (nameoff < sizeof (struct grub_erofs_dirent) || nameoff >= maxsize) + { + grub_error (GRUB_ERR_BAD_FS, + "invalid nameoff %u @ inode %" PRIuGRUB_UINT64_T, + nameoff, dir->ino); + goto not_found; + } + + end = (struct grub_erofs_dirent *) ((grub_uint8_t *) de + nameoff); + while (de < end) + { + struct grub_fshelp_node *fdiro; + enum grub_fshelp_filetype type; + char filename[EROFS_NAME_LEN + 1]; + grub_size_t de_namelen; + const char *de_name; + + fdiro = grub_malloc (sizeof (struct grub_fshelp_node)); + if (fdiro == NULL) + goto not_found; + + fdiro->data = dir->data; + fdiro->ino = grub_le_to_cpu64 (de->nid); + fdiro->inode_loaded = false; + + nameoff = grub_le_to_cpu16 (de->nameoff); + if (nameoff < sizeof (struct grub_erofs_dirent) || nameoff >= maxsize) + { + grub_error (GRUB_ERR_BAD_FS, + "invalid nameoff %u @ inode %" PRIuGRUB_UINT64_T, + nameoff, dir->ino); + grub_free (fdiro); + goto not_found; + } + + de_name = (char *) buf + nameoff; + if (de + 1 >= end) + de_namelen = grub_erofs_strnlen (de_name, maxsize - nameoff); + else + { + if (grub_sub (grub_le_to_cpu16 (de[1].nameoff), nameoff, &de_namelen)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "de_namelen underflow"); + grub_free (fdiro); + goto not_found; + } + } + + if (nameoff + de_namelen > maxsize || de_namelen > EROFS_NAME_LEN) + { + grub_error (GRUB_ERR_BAD_FS, + "invalid de_namelen %" PRIuGRUB_SIZE + " @ inode %" PRIuGRUB_UINT64_T, + de_namelen, dir->ino); + grub_free (fdiro); + goto not_found; + } + + grub_memcpy (filename, de_name, de_namelen); + filename[de_namelen] = '\0'; + + switch (grub_le_to_cpu16 (de->file_type)) + { + case EROFS_FT_REG_FILE: + case EROFS_FT_BLKDEV: + case EROFS_FT_CHRDEV: + case EROFS_FT_FIFO: + case EROFS_FT_SOCK: + type = GRUB_FSHELP_REG; + break; + case EROFS_FT_DIR: + type = GRUB_FSHELP_DIR; + break; + case EROFS_FT_SYMLINK: + type = GRUB_FSHELP_SYMLINK; + break; + case EROFS_FT_UNKNOWN: + default: + type = GRUB_FSHELP_UNKNOWN; + } + + if (hook (filename, type, fdiro, hook_data)) + { + grub_free (buf); + return 1; + } + + ++de; + } + + offset += maxsize; + } + + not_found: + grub_free (buf); + return 0; +} + +static char * +erofs_read_symlink (grub_fshelp_node_t node) +{ + char *symlink; + grub_size_t sz; + grub_err_t err; + + if (node->inode_loaded == false) + { + err = erofs_read_inode (node->data, node); + if (err != GRUB_ERR_NONE) + return NULL; + } + + sz = erofs_inode_file_size (node); + if (sz >= EROFS_PATH_LEN) + { + grub_error (GRUB_ERR_BAD_FS, + "symlink too long @ inode %" PRIuGRUB_UINT64_T, node->ino); + return NULL; + } + + symlink = grub_malloc (sz + 1); + if (symlink == NULL) + return NULL; + + err = erofs_read_raw_data (node, (grub_uint8_t *) symlink, sz, 0, NULL); + if (err != GRUB_ERR_NONE) + { + grub_free (symlink); + return NULL; + } + + symlink[sz] = '\0'; + return symlink; +} + +static struct grub_erofs_data * +erofs_mount (grub_disk_t disk, bool read_root) +{ + struct grub_erofs_super sb; + grub_err_t err; + struct grub_erofs_data *data; + grub_uint32_t feature; + + err = grub_disk_read (disk, EROFS_SUPER_OFFSET >> GRUB_DISK_SECTOR_BITS, 0, + sizeof (sb), &sb); + if (err != GRUB_ERR_NONE) + return NULL; + if (sb.magic != grub_cpu_to_le32_compile_time (EROFS_MAGIC) || + grub_le_to_cpu32 (sb.log2_blksz) < EROFS_MIN_LOG2_BLOCK_SIZE || + grub_le_to_cpu32 (sb.log2_blksz) > EROFS_MAX_LOG2_BLOCK_SIZE) + { + grub_error (GRUB_ERR_BAD_FS, "not a valid erofs filesystem"); + return NULL; + } + + feature = grub_le_to_cpu32 (sb.feature_incompat); + if (feature & ~EROFS_ALL_FEATURE_INCOMPAT) + { + grub_error (GRUB_ERR_BAD_FS, "unsupported features: 0x%x", + feature & ~EROFS_ALL_FEATURE_INCOMPAT); + return NULL; + } + + data = grub_malloc (sizeof (*data)); + if (data == NULL) + return NULL; + + data->disk = disk; + data->sb = sb; + + if (read_root) + { + data->inode.data = data; + data->inode.ino = grub_le_to_cpu16 (sb.root_nid); + err = erofs_read_inode (data, &data->inode); + if (err != GRUB_ERR_NONE) + { + grub_free (data); + return NULL; + } + } + + return data; +} + +/* Context for grub_erofs_dir. */ +struct grub_erofs_dir_ctx +{ + grub_fs_dir_hook_t hook; + void *hook_data; + struct grub_erofs_data *data; +}; + +/* Helper for grub_erofs_dir. */ +static int +erofs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node, void *data) +{ + struct grub_erofs_dir_ctx *ctx = data; + struct grub_dirhook_info info = {0}; + grub_err_t err; + + if (node->inode_loaded == false) + { + err = erofs_read_inode (ctx->data, node); + if (err != GRUB_ERR_NONE) + return 0; + } + + if (node->inode_loaded == true) + { + info.mtimeset = 1; + info.mtime = erofs_inode_mtime (node); + } + + info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); + grub_free (node); + return ctx->hook (filename, &info, ctx->hook_data); +} + +static grub_err_t +grub_erofs_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook, + void *hook_data) +{ + grub_fshelp_node_t fdiro = NULL; + grub_err_t err; + struct grub_erofs_dir_ctx ctx = { + .hook = hook, + .hook_data = hook_data + }; + + ctx.data = erofs_mount (device->disk, true); + if (ctx.data == NULL) + goto fail; + + err = grub_fshelp_find_file (path, &ctx.data->inode, &fdiro, erofs_iterate_dir, + erofs_read_symlink, GRUB_FSHELP_DIR); + if (err != GRUB_ERR_NONE) + goto fail; + + erofs_iterate_dir (fdiro, erofs_dir_iter, &ctx); + + fail: + if (fdiro != &ctx.data->inode) + grub_free (fdiro); + grub_free (ctx.data); + + return grub_errno; +} + +static grub_err_t +grub_erofs_open (grub_file_t file, const char *name) +{ + struct grub_erofs_data *data; + struct grub_fshelp_node *fdiro = NULL; + grub_err_t err; + + data = erofs_mount (file->device->disk, true); + if (data == NULL) + { + err = grub_errno; + goto fail; + } + + err = grub_fshelp_find_file (name, &data->inode, &fdiro, erofs_iterate_dir, + erofs_read_symlink, GRUB_FSHELP_REG); + if (err != GRUB_ERR_NONE) + goto fail; + + if (fdiro->inode_loaded == false) + { + err = erofs_read_inode (data, fdiro); + if (err != GRUB_ERR_NONE) + goto fail; + } + + grub_memcpy (&data->inode, fdiro, sizeof (*fdiro)); + grub_free (fdiro); + + file->data = data; + file->size = erofs_inode_file_size (&data->inode); + + return GRUB_ERR_NONE; + + fail: + if (fdiro != &data->inode) + grub_free (fdiro); + grub_free (data); + + return err; +} + +static grub_ssize_t +grub_erofs_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_erofs_data *data = file->data; + struct grub_fshelp_node *inode = &data->inode; + grub_off_t off = file->offset; + grub_uint64_t ret = 0, file_size; + grub_err_t err; + + if (inode->inode_loaded == false) + { + err = erofs_read_inode (data, inode); + if (err != GRUB_ERR_NONE) + return -1; + } + + file_size = erofs_inode_file_size (inode); + + if (off > file_size) + { + grub_error (GRUB_ERR_IO, "read past EOF @ inode %" PRIuGRUB_UINT64_T, inode->ino); + return -1; + } + if (off == file_size) + return 0; + + if (off + len > file_size) + len = file_size - off; + + err = erofs_read_raw_data (inode, (grub_uint8_t *) buf, len, off, &ret); + if (err != GRUB_ERR_NONE) + return -1; + + return ret; +} + +static grub_err_t +grub_erofs_close (grub_file_t file) +{ + grub_free (file->data); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_erofs_uuid (grub_device_t device, char **uuid) +{ + struct grub_erofs_data *data; + + data = erofs_mount (device->disk, false); + if (data == NULL) + { + *uuid = NULL; + return grub_errno; + } + + *uuid = grub_xasprintf ("%pG", &data->sb.uuid); + + grub_free (data); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_erofs_label (grub_device_t device, char **label) +{ + struct grub_erofs_data *data; + + data = erofs_mount (device->disk, false); + if (data == NULL) + { + *label = NULL; + return grub_errno; + } + + *label = grub_strndup ((char *) data->sb.volume_name, sizeof (data->sb.volume_name)); + grub_free (data); + + if (*label == NULL) + return grub_errno; + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_erofs_mtime (grub_device_t device, grub_int64_t *tm) +{ + struct grub_erofs_data *data; + + data = erofs_mount (device->disk, false); + if (data == NULL) + { + *tm = 0; + return grub_errno; + } + + *tm = grub_le_to_cpu64 (data->sb.build_time); + + grub_free (data); + + return GRUB_ERR_NONE; +} + +static struct grub_fs grub_erofs_fs = { + .name = "erofs", + .fs_dir = grub_erofs_dir, + .fs_open = grub_erofs_open, + .fs_read = grub_erofs_read, + .fs_close = grub_erofs_close, + .fs_uuid = grub_erofs_uuid, + .fs_label = grub_erofs_label, + .fs_mtime = grub_erofs_mtime, +#ifdef GRUB_UTIL + .reserved_first_sector = 1, + .blocklist_install = 0, +#endif + .next = 0, +}; + +GRUB_MOD_INIT (erofs) +{ + grub_fs_register (&grub_erofs_fs); +} + +GRUB_MOD_FINI (erofs) +{ + grub_fs_unregister (&grub_erofs_fs); +} From 56e58828cf3cd32ba4768779accc6655120c3136 Mon Sep 17 00:00:00 2001 From: Yifan Zhao Date: Tue, 21 May 2024 01:20:59 +0800 Subject: [PATCH 137/402] fs/erofs: Add tests for EROFS in grub-fs-tester This patch introduces three EROFS tests which cover compact, extended and chunk-based inodes respectively. Signed-off-by: Yifan Zhao Reviewed-by: Glenn Washburn Signed-off-by: Gao Xiang Reviewed-by: Daniel Kiper --- .gitignore | 1 + Makefile.util.def | 6 ++++++ tests/erofs_test.in | 20 ++++++++++++++++++++ tests/util/grub-fs-tester.in | 32 +++++++++++++++++++++++++------- 4 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 tests/erofs_test.in diff --git a/.gitignore b/.gitignore index 11fcecf5c..4c1f91db8 100644 --- a/.gitignore +++ b/.gitignore @@ -104,6 +104,7 @@ widthspec.bin /docs/version-dev.texi /docs/version.texi /ehci_test +/erofs_test /example_grub_script_test /example_scripted_test /example_unit_test diff --git a/Makefile.util.def b/Makefile.util.def index 8d3bc107f..0f74a1680 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -764,6 +764,12 @@ script = { dependencies = 'garbage-gen$(BUILD_EXEEXT)'; }; +script = { + testcase = native; + name = erofs_test; + common = tests/erofs_test.in; +}; + script = { testcase = native; name = ext234_test; diff --git a/tests/erofs_test.in b/tests/erofs_test.in new file mode 100644 index 000000000..51111627a --- /dev/null +++ b/tests/erofs_test.in @@ -0,0 +1,20 @@ +#!@BUILD_SHEBANG@ + +set -e + +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 99 +fi + +"@builddir@/grub-fs-tester" erofs_compact +"@builddir@/grub-fs-tester" erofs_extended +"@builddir@/grub-fs-tester" erofs_chunk diff --git a/tests/util/grub-fs-tester.in b/tests/util/grub-fs-tester.in index ea8b2d1f6..df5dc7542 100644 --- a/tests/util/grub-fs-tester.in +++ b/tests/util/grub-fs-tester.in @@ -227,6 +227,10 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do xsquash*) MINBLKSIZE=4096 MAXBLKSIZE=1048576;; + x"erofs_"*) + MINBLKSIZE=4096 + MAXBLKSIZE=4096 + ;; xxfs|xf2fs) MINBLKSIZE=$SECSIZE # OS Limitation: GNU/Linux doesn't accept > 4096 @@ -382,8 +386,8 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do FSLABEL="g;/_Ă©äŒä“ä•äŽ›äŽŸäŽĐșот u" #FSLABEL="g;/_Ă©đŻŠ›đŻŠđŸ˜Đșот u" ;; - # 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") + # 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_"*) 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") @@ -661,7 +665,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do x"tarfs" | x"cpio_"*| x"ziso9660" | x"romfs" | x"squash4_"*\ | x"iso9660" | xjoliet | xrockridge | xrockridge_joliet \ | x"iso9660_1999" | xjoliet_1999 | xrockridge_1999 \ - | xrockridge_joliet_1999) + | xrockridge_joliet_1999 | x"erofs_"*) MNTPOINTRW="$MASTER" MNTPOINTRO="$MASTER" mkdir -p "$MASTER";; @@ -805,7 +809,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do sleep 1 "zfs" create "$FSLABEL"/"grub fs" sleep 1;; - x"tarfs" | x"cpio_"* | x"iso9660" | xjoliet | xrockridge | xrockridge_joliet | x"iso9660_1999" | xjoliet_1999 | xrockridge_1999 | xrockridge_joliet_1999 | x"ziso9660" | x"romfs" | x"squash4_"*) + x"tarfs" | x"cpio_"* | x"iso9660" | xjoliet | xrockridge | xrockridge_joliet | x"iso9660_1999" | xjoliet_1999 | xrockridge_1999 | xrockridge_joliet_1999 | x"ziso9660" | x"romfs" | x"squash4_"* | x"erofs_"*) INSTDEVICE=/dev/null;; x"reiserfs") "mkfs.reiserfs" --format=3.6 -b $BLKSIZE -l "$FSLABEL" -q "${MOUNTDEVICE}" ;; @@ -990,7 +994,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do x"zfs"*) OSDIR="grub fs/" GRUBDIR="($GRUBDEVICE)/grub fs@";; - x"tarfs" | x"cpio_"* | x"iso9660" | xjoliet | xrockridge | xrockridge_joliet | x"iso9660_1999" | xjoliet_1999 | xrockridge_1999 | xrockridge_joliet_1999 | x"ziso9660" | x"romfs" | x"squash4_"* | xafs) + x"tarfs" | x"cpio_"* | x"iso9660" | xjoliet | xrockridge | xrockridge_joliet | x"iso9660_1999" | xjoliet_1999 | xrockridge_1999 | xrockridge_joliet_1999 | x"ziso9660" | x"romfs" | x"squash4_"* | xafs | x"erofs_"*) ;; *) if ! mount -t "$MOUNTFS" "${MOUNTDEVICE}" "$MNTPOINTRW" -o ${MOUNTOPTS}${SELINUXOPTS}rw ; then @@ -1116,6 +1120,18 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do xsquash4_*) echo mksquashfs "$MASTER" "${FSIMAGEP}0.img" -always-use-fragments -comp "${fs/squash4_/}" -b $BLKSIZE mksquashfs "$MASTER" "${FSIMAGEP}0.img" -always-use-fragments -comp "${fs/squash4_/}" -b $BLKSIZE ;; + x"erofs_compact") + echo mkfs.erofs -Eforce-inode-compact -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW" + mkfs.erofs -Eforce-inode-compact -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW" + ;; + x"erofs_extended") + echo mkfs.erofs -Eforce-inode-extended -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW" + mkfs.erofs -Eforce-inode-extended -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW" + ;; + x"erofs_chunk") + echo mkfs.erofs --chunksize=1048576 -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW" + mkfs.erofs --chunksize=1048576 -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW" + ;; x"bfs") sleep 1 fusermount -u "$MNTPOINTRW" @@ -1187,6 +1203,8 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do ;; xsquash4_*) ;; + x"erofs_"*) + ;; xlvm*) vgchange -a y grub_test sleep 1 @@ -1624,7 +1642,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do sleep 1; done sleep 5;; - x"tarfs" | x"cpio_"* | xrockridge | xjoliet | xrockridge_joliet | x"ziso9660" | x"romfs" | x"squash4_"* | xiso9660 | xiso9660_1999 | xrockridge_1999 | xjoliet_1999 | xrockridge_joliet_1999) + x"tarfs" | x"cpio_"* | xrockridge | xjoliet | xrockridge_joliet | x"ziso9660" | x"romfs" | x"squash4_"* | xiso9660 | xiso9660_1999 | xrockridge_1999 | xjoliet_1999 | xrockridge_joliet_1999 | x"erofs_"*) rm -rf "$MNTPOINTRW";; x"afs") rm -rf "$MNTPOINTRO" @@ -1651,7 +1669,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do ;; esac case x"$fs" in - x"tarfs" | x"cpio_"* | x"iso9660" | xrockridge | xjoliet | xrockridge_joliet | x"ziso9660" | x"romfs" | x"squash4_"* | x"iso9660_1999" | xrockridge_1999 | xjoliet_1999 | xrockridge_joliet_1999) ;; + x"tarfs" | x"cpio_"* | x"iso9660" | xrockridge | xjoliet | xrockridge_joliet | x"ziso9660" | x"romfs" | x"squash4_"* | x"iso9660_1999" | xrockridge_1999 | xjoliet_1999 | xrockridge_joliet_1999 | x"erofs_"*) ;; *) for lodev in $LODEVICES; do while ! losetup -d "$lodev"; do From bb65d81fe320e4b20d0a9b32232a7546eb275ecc Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Wed, 24 Jan 2024 06:26:37 +0000 Subject: [PATCH 138/402] cli_lock: Add build option to block command line interface Add functionality to disable command line interface access and editing of GRUB menu entries if GRUB image is built with --disable-cli. Signed-off-by: Alec Brown Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- docs/grub.texi | 6 ++++-- grub-core/kern/main.c | 28 ++++++++++++++++++++++++++++ grub-core/kern/rescue_reader.c | 13 +++++++++++++ grub-core/normal/auth.c | 3 +++ grub-core/normal/menu_text.c | 31 +++++++++++++++++-------------- include/grub/kernel.h | 3 ++- include/grub/misc.h | 2 ++ include/grub/util/install.h | 8 ++++++-- util/grub-install-common.c | 11 ++++++++--- util/grub-mkimage.c | 9 ++++++++- util/mkimage.c | 16 +++++++++++++++- 11 files changed, 106 insertions(+), 24 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index cb3a14fca..f3bdc2564 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -6422,8 +6422,10 @@ the GRUB command line, edit menu entries, and execute any menu entry. If @samp{superusers} is set, then use of the command line and editing of menu entries are automatically restricted to superusers. Setting @samp{superusers} to empty string effectively disables both access to CLI and editing of menu -entries. Note: The environment variable needs to be exported to also affect -the section defined by the @samp{submenu} command (@pxref{submenu}). +entries. Building a grub image with @samp{--disable-cli} option will also +disable access to CLI and editing of menu entries, as well as disabling rescue +mode. Note: The environment variable needs to be exported to also affect the +section defined by the @samp{submenu} command (@pxref{submenu}). Other users may be allowed to execute specific menu entries by giving a list of usernames (as above) using the @option{--users} option to the diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c index 744197785..d29494d54 100644 --- a/grub-core/kern/main.c +++ b/grub-core/kern/main.c @@ -31,11 +31,14 @@ #include #include #include +#include #ifdef GRUB_MACHINE_PCBIOS #include #endif +static bool cli_disabled = false; + grub_addr_t grub_modules_get_end (void) { @@ -238,6 +241,28 @@ grub_load_normal_mode (void) grub_command_execute ("normal", 0, 0); } +bool +grub_is_cli_disabled (void) +{ + return cli_disabled; +} + +static void +check_is_cli_disabled (void) +{ + struct grub_module_header *header; + header = 0; + + FOR_MODULES (header) + { + if (header->type == OBJ_TYPE_DISABLE_CLI) + { + cli_disabled = true; + return; + } + } +} + static void reclaim_module_space (void) { @@ -305,6 +330,9 @@ grub_main (void) grub_boot_time ("After loading embedded modules."); + /* Check if the CLI should be disabled */ + check_is_cli_disabled (); + /* It is better to set the root device as soon as possible, for convenience. */ grub_set_prefix_and_root (); diff --git a/grub-core/kern/rescue_reader.c b/grub-core/kern/rescue_reader.c index dcd7d4439..4259857ba 100644 --- a/grub-core/kern/rescue_reader.c +++ b/grub-core/kern/rescue_reader.c @@ -78,6 +78,19 @@ grub_rescue_read_line (char **line, int cont, void __attribute__ ((noreturn)) grub_rescue_run (void) { + /* Stall if the CLI has been disabled */ + if (grub_is_cli_disabled ()) + { + grub_printf ("Rescue mode has been disabled...\n"); + + do + { + /* Do not optimize out the loop. */ + asm volatile (""); + } + while (1); + } + grub_printf ("Entering rescue mode...\n"); while (1) diff --git a/grub-core/normal/auth.c b/grub-core/normal/auth.c index 517fc623f..d94020186 100644 --- a/grub-core/normal/auth.c +++ b/grub-core/normal/auth.c @@ -209,6 +209,9 @@ grub_auth_check_authentication (const char *userlist) char entered[GRUB_AUTH_MAX_PASSLEN]; struct grub_auth_user *user; + if (grub_is_cli_disabled ()) + return GRUB_ACCESS_DENIED; + grub_memset (login, 0, sizeof (login)); if (is_authenticated (userlist)) diff --git a/grub-core/normal/menu_text.c b/grub-core/normal/menu_text.c index b1321eb26..9c383e64a 100644 --- a/grub-core/normal/menu_text.c +++ b/grub-core/normal/menu_text.c @@ -178,21 +178,24 @@ command-line or ESC to discard edits and return to the GRUB menu."), grub_free (msg_translated); - if (nested) + if (!grub_is_cli_disabled ()) { - ret += grub_print_message_indented_real - (_("Press enter to boot the selected OS, " - "`e' to edit the commands before booting " - "or `c' for a command-line. ESC to return previous menu."), - STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run); - } - else - { - ret += grub_print_message_indented_real - (_("Press enter to boot the selected OS, " - "`e' to edit the commands before booting " - "or `c' for a command-line."), - STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run); + if (nested) + { + ret += grub_print_message_indented_real + (_("Press enter to boot the selected OS, " + "`e' to edit the commands before booting " + "or `c' for a command-line. ESC to return previous menu."), + STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run); + } + else + { + ret += grub_print_message_indented_real + (_("Press enter to boot the selected OS, " + "`e' to edit the commands before booting " + "or `c' for a command-line."), + STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run); + } } } return ret; diff --git a/include/grub/kernel.h b/include/grub/kernel.h index abbca5ea3..6121c1e66 100644 --- a/include/grub/kernel.h +++ b/include/grub/kernel.h @@ -30,7 +30,8 @@ enum OBJ_TYPE_PREFIX, OBJ_TYPE_PUBKEY, OBJ_TYPE_DTB, - OBJ_TYPE_DISABLE_SHIM_LOCK + OBJ_TYPE_DISABLE_SHIM_LOCK, + OBJ_TYPE_DISABLE_CLI }; /* The module header. */ diff --git a/include/grub/misc.h b/include/grub/misc.h index 1b35a167f..1578f36c3 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -391,6 +391,8 @@ grub_uint64_t EXPORT_FUNC(grub_divmod64) (grub_uint64_t n, grub_uint64_t d, grub_uint64_t *r); +extern bool EXPORT_FUNC(grub_is_cli_disabled) (void); + /* Must match softdiv group in gentpl.py. */ #if !defined(GRUB_MACHINE_EMU) && (defined(__arm__) || defined(__ia64__) || \ (defined(__riscv) && (__riscv_xlen == 32))) diff --git a/include/grub/util/install.h b/include/grub/util/install.h index 35cf17a8d..5c0a52ca2 100644 --- a/include/grub/util/install.h +++ b/include/grub/util/install.h @@ -67,6 +67,8 @@ 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_("disabled command line interface access"), 0 }, \ { "verbose", 'v', 0, 0, \ N_("print verbose messages."), 1 } @@ -129,7 +131,8 @@ enum grub_install_options { GRUB_INSTALL_OPTIONS_INSTALL_CORE_COMPRESS, GRUB_INSTALL_OPTIONS_DTB, GRUB_INSTALL_OPTIONS_SBAT, - GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK + GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, + GRUB_INSTALL_OPTIONS_DISABLE_CLI }; extern char *grub_install_source_directory; @@ -191,7 +194,8 @@ grub_install_generate_image (const char *dir, const char *prefix, const struct grub_install_image_target_desc *image_target, int note, grub_compression_t comp, const char *dtb_file, - const char *sbat_path, const int disable_shim_lock); + const char *sbat_path, const int disable_shim_lock, + const int disable_cli); const struct grub_install_image_target_desc * grub_install_get_image_target (const char *arg); diff --git a/util/grub-install-common.c b/util/grub-install-common.c index ce854d86f..22bccb6a3 100644 --- a/util/grub-install-common.c +++ b/util/grub-install-common.c @@ -466,6 +466,7 @@ static size_t npubkeys; static char *sbat; static int disable_shim_lock; static grub_compression_t compression; +static int disable_cli; int grub_install_parse (int key, char *arg) @@ -504,6 +505,9 @@ grub_install_parse (int key, char *arg) case GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK: disable_shim_lock = 1; return 1; + case GRUB_INSTALL_OPTIONS_DISABLE_CLI: + disable_cli = 1; + return 1; case GRUB_INSTALL_OPTIONS_VERBOSITY: verbosity++; @@ -679,11 +683,12 @@ 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\n", + " --format '%s' --compression '%s'%s%s%s%s\n", dir, prefix, outname, mkimage_target, compnames[compression], note ? " --note" : "", - disable_shim_lock ? " --disable-shim-lock" : "", s); + disable_shim_lock ? " --disable-shim-lock" : "", + disable_cli ? " --disable-cli" : "", s); free (s); tgt = grub_install_get_image_target (mkimage_target); @@ -694,7 +699,7 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix, modules.entries, memdisk_path, pubkeys, npubkeys, config_path, tgt, note, compression, dtb, sbat, - disable_shim_lock); + disable_shim_lock, disable_cli); while (dc--) grub_install_pop_module (); } diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c index c0d559937..547f7310f 100644 --- a/util/grub-mkimage.c +++ b/util/grub-mkimage.c @@ -83,6 +83,7 @@ static struct argp_option options[] = { {"compression", 'C', "(xz|none|auto)", 0, N_("choose the compression to use for core image"), 0}, {"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}, {"verbose", 'v', 0, 0, N_("print verbose messages."), 0}, { 0, 0, 0, 0, 0, 0 } }; @@ -128,6 +129,7 @@ struct arguments char *sbat; int note; int disable_shim_lock; + int disable_cli; const struct grub_install_image_target_desc *image_target; grub_compression_t comp; }; @@ -239,6 +241,10 @@ argp_parser (int key, char *arg, struct argp_state *state) arguments->disable_shim_lock = 1; break; + case GRUB_INSTALL_OPTIONS_DISABLE_CLI: + arguments->disable_cli = 1; + break; + case 'v': verbosity++; break; @@ -325,7 +331,8 @@ main (int argc, char *argv[]) arguments.npubkeys, arguments.config, arguments.image_target, arguments.note, arguments.comp, arguments.dtb, - arguments.sbat, arguments.disable_shim_lock); + arguments.sbat, arguments.disable_shim_lock, + arguments.disable_cli); if (grub_util_file_sync (fp) < 0) grub_util_error (_("cannot sync `%s': %s"), arguments.output ? : "stdout", diff --git a/util/mkimage.c b/util/mkimage.c index 4237383ac..8c5660825 100644 --- a/util/mkimage.c +++ b/util/mkimage.c @@ -886,7 +886,8 @@ 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, grub_compression_t comp, const char *dtb_path, - const char *sbat_path, int disable_shim_lock) + const char *sbat_path, int disable_shim_lock, + int disable_cli) { char *kernel_img, *core_img; size_t total_module_size, core_size; @@ -948,6 +949,9 @@ grub_install_generate_image (const char *dir, const char *prefix, if (disable_shim_lock) total_module_size += sizeof (struct grub_module_header); + if (disable_cli) + total_module_size += sizeof (struct grub_module_header); + if (config_path) { config_size = ALIGN_ADDR (grub_util_get_image_size (config_path) + 1); @@ -1094,6 +1098,16 @@ grub_install_generate_image (const char *dir, const char *prefix, offset += sizeof (*header); } + if (disable_cli) + { + struct grub_module_header *header; + + header = (struct grub_module_header *) (kernel_img + offset); + header->type = grub_host_to_target32 (OBJ_TYPE_DISABLE_CLI); + header->size = grub_host_to_target32 (sizeof (*header)); + offset += sizeof (*header); + } + if (config_path) { struct grub_module_header *header; From 243682baaa31b760d110acd20fa397d2e8616ebc Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Mon, 13 May 2024 00:32:08 +1000 Subject: [PATCH 139/402] io/gzio: Abort early when get_byte() reads nothing This isn't intended to be a functional change, but it makes a lot of failures a lot faster, which is extremely helpful for fuzzing. Without this change, we keep trying and trying to read more bytes into our buffer, never being able to (read always returns 0) and so we just return old buffer contents over and over until the decompression process fails some other way. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper --- grub-core/io/gzio.c | 55 +++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/grub-core/io/gzio.c b/grub-core/io/gzio.c index ca9355751..b882b559a 100644 --- a/grub-core/io/gzio.c +++ b/grub-core/io/gzio.c @@ -366,6 +366,10 @@ static int dbits = 6; /* bits in base distance lookup table */ lookup of seven bits, the EOB code will be found in that first lookup, and so will not require that too many bits be pulled from the stream. + + Note that NEEDBITS() can fail if the file is corrupt/truncated. + The GOTOFAILIFERROR, RETURNIFERROR and RETURN1IFERROR macros can + help. */ static ush mask_bits[] = @@ -377,10 +381,14 @@ static ush mask_bits[] = #define NEEDBITS(n) do {while(k<(n)){b|=((ulg)get_byte(gzio))<>=(n);k-=(n);} while (0) +#define RETURNIFERROR if (grub_errno != GRUB_ERR_NONE) return +#define RETURN1IFERROR if (grub_errno != GRUB_ERR_NONE) return 1 +#define GOTOFAILIFERROR if (grub_errno != GRUB_ERR_NONE) goto fail static int get_byte (grub_gzio_t gzio) { + grub_ssize_t bytes_read; if (gzio->mem_input) { if (gzio->mem_input_off < gzio->mem_input_size) @@ -393,7 +401,16 @@ get_byte (grub_gzio_t gzio) || gzio->inbuf_d == INBUFSIZ)) { gzio->inbuf_d = 0; - grub_file_read (gzio->file, gzio->inbuf, INBUFSIZ); + bytes_read = grub_file_read (gzio->file, gzio->inbuf, INBUFSIZ); + /* + * Only trigger if we get 0 bytes. If we get negative bytes there should + * be an existing GRUB error code that we don't want to overwrite. + */ + if (bytes_read == 0) + { + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "File is too short"); + return 0; + } } return gzio->inbuf[gzio->inbuf_d++]; @@ -684,7 +701,7 @@ inflate_codes_in_window (grub_gzio_t gzio) return 1; } - NEEDBITS ((unsigned) gzio->bl); + NEEDBITS ((unsigned) gzio->bl); RETURN1IFERROR; if ((e = (t = gzio->tl + ((unsigned) b & ml))->e) > 16) do { @@ -696,7 +713,7 @@ inflate_codes_in_window (grub_gzio_t gzio) } DUMPBITS (t->b); e -= 16; - NEEDBITS (e); + NEEDBITS (e); RETURN1IFERROR; } while ((e = (t = t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16); DUMPBITS (t->b); @@ -718,7 +735,7 @@ inflate_codes_in_window (grub_gzio_t gzio) } /* get length of block to copy */ - NEEDBITS (e); + NEEDBITS (e); RETURN1IFERROR; n = t->v.n + ((unsigned) b & mask_bits[e]); DUMPBITS (e); @@ -729,7 +746,7 @@ inflate_codes_in_window (grub_gzio_t gzio) } /* decode distance of block to copy */ - NEEDBITS ((unsigned) gzio->bd); + NEEDBITS ((unsigned) gzio->bd); RETURN1IFERROR; if ((e = (t = gzio->td + ((unsigned) b & md))->e) > 16) do { @@ -741,12 +758,12 @@ inflate_codes_in_window (grub_gzio_t gzio) } DUMPBITS (t->b); e -= 16; - NEEDBITS (e); + NEEDBITS (e); RETURN1IFERROR; } while ((e = (t = t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16); DUMPBITS (t->b); - NEEDBITS (e); + NEEDBITS (e); RETURN1IFERROR; d = w - t->v.n - ((unsigned) b & mask_bits[e]); DUMPBITS (e); gzio->code_state++; @@ -815,10 +832,10 @@ init_stored_block (grub_gzio_t gzio) DUMPBITS (k & 7); /* get the length and its complement */ - NEEDBITS (16); + NEEDBITS (16); RETURNIFERROR; gzio->block_len = ((unsigned) b & 0xffff); DUMPBITS (16); - NEEDBITS (16); + NEEDBITS (16); RETURNIFERROR; if (gzio->block_len != (int) ((~b) & 0xffff)) grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "the length of a stored block does not match"); @@ -900,13 +917,13 @@ init_dynamic_block (grub_gzio_t gzio) k = gzio->bk; /* read in table lengths */ - NEEDBITS (5); + NEEDBITS (5); RETURNIFERROR; nl = 257 + ((unsigned) b & 0x1f); /* number of literal/length codes */ DUMPBITS (5); - NEEDBITS (5); + NEEDBITS (5); RETURNIFERROR; nd = 1 + ((unsigned) b & 0x1f); /* number of distance codes */ DUMPBITS (5); - NEEDBITS (4); + NEEDBITS (4); RETURNIFERROR; nb = 4 + ((unsigned) b & 0xf); /* number of bit length codes */ DUMPBITS (4); if (nl > 286 || nd > 30) @@ -918,7 +935,7 @@ init_dynamic_block (grub_gzio_t gzio) /* read in bit-length-code lengths */ for (j = 0; j < nb; j++) { - NEEDBITS (3); + NEEDBITS (3); RETURNIFERROR; ll[bitorder[j]] = (unsigned) b & 7; DUMPBITS (3); } @@ -947,7 +964,7 @@ init_dynamic_block (grub_gzio_t gzio) while ((unsigned) i < n) { - NEEDBITS ((unsigned) gzio->bl); + NEEDBITS ((unsigned) gzio->bl); GOTOFAILIFERROR; j = (gzio->td = gzio->tl + ((unsigned) b & m))->b; DUMPBITS (j); j = gzio->td->v.n; @@ -955,7 +972,7 @@ init_dynamic_block (grub_gzio_t gzio) ll[i++] = l = j; /* save last length in l */ else if (j == 16) /* repeat last length 3 to 6 times */ { - NEEDBITS (2); + NEEDBITS (2); GOTOFAILIFERROR; j = 3 + ((unsigned) b & 3); DUMPBITS (2); if ((unsigned) i + j > n) @@ -968,7 +985,7 @@ init_dynamic_block (grub_gzio_t gzio) } else if (j == 17) /* 3 to 10 zero length codes */ { - NEEDBITS (3); + NEEDBITS (3); GOTOFAILIFERROR; j = 3 + ((unsigned) b & 7); DUMPBITS (3); if ((unsigned) i + j > n) @@ -983,7 +1000,7 @@ init_dynamic_block (grub_gzio_t gzio) else /* j == 18: 11 to 138 zero length codes */ { - NEEDBITS (7); + NEEDBITS (7); GOTOFAILIFERROR; j = 11 + ((unsigned) b & 0x7f); DUMPBITS (7); if ((unsigned) i + j > n) @@ -1049,12 +1066,12 @@ get_new_block (grub_gzio_t gzio) k = gzio->bk; /* read in last block bit */ - NEEDBITS (1); + NEEDBITS (1); RETURNIFERROR; gzio->last_block = (int) b & 1; DUMPBITS (1); /* read in block type */ - NEEDBITS (2); + NEEDBITS (2); RETURNIFERROR; gzio->block_type = (unsigned) b & 3; DUMPBITS (2); From c806e4dc8895ec9ed110480034344cce25994816 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Mon, 13 May 2024 00:32:09 +1000 Subject: [PATCH 140/402] io/gzio: Properly init a table ARRAY_SIZE() is the count of elements, but the element size is 4 bytes, so this was only initing the first 1/4th of the table. Detected with valgrind. This should only matter in error paths, and I've not been able to identify any actual misbehaviour that results from reading in-bounds but uninited data. Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper --- grub-core/io/gzio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/io/gzio.c b/grub-core/io/gzio.c index b882b559a..cb7eb1fe3 100644 --- a/grub-core/io/gzio.c +++ b/grub-core/io/gzio.c @@ -524,7 +524,7 @@ huft_build (unsigned *b, /* code lengths in bits (all assumed <= BMAX) */ } /* Make a table of values in order of bit lengths */ - grub_memset (v, N_MAX, ARRAY_SIZE (v)); + grub_memset (v, N_MAX, sizeof (v)); p = b; i = 0; do From 5e8989e4eda6fd8b2b497b35e20365b254212f30 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Thu, 16 May 2024 21:37:49 +0300 Subject: [PATCH 141/402] fs/bfs: Fix improper grub_free() on non-existing files Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/fs/bfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/fs/bfs.c b/grub-core/fs/bfs.c index 07cb3e3ac..9bc478ce8 100644 --- a/grub-core/fs/bfs.c +++ b/grub-core/fs/bfs.c @@ -808,7 +808,7 @@ find_file (const char *path, grub_disk_t disk, .disk = disk, .sb = sb, }; - struct grub_fshelp_node *found; + struct grub_fshelp_node *found = NULL; err = read_extent (disk, sb, &sb->root_dir, 0, 0, &root.ino, sizeof (root.ino)); From c6ac4912043c2b21ecd790a2a06e7557cda25449 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Thu, 16 May 2024 22:03:29 +0300 Subject: [PATCH 142/402] font: Add Fedora-specific font paths Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 84a202c6e..99aae8700 100644 --- a/configure.ac +++ b/configure.ac @@ -1847,7 +1847,7 @@ if test "x$with_dejavufont" = x; then # search in well-known directories if test x"$starfield_excuse" = x; then for ext in pcf pcf.gz bdf bdf.gz ttf ttf.gz; do - for dir in . /usr/src /usr/share/fonts/X11/misc /usr/share/fonts/truetype/ttf-dejavu /usr/share/fonts/dejavu /usr/share/fonts/truetype /usr/pkg/share/fonts/X11/TTF /usr/local/share/fonts/dejavu /usr/X11R6/lib/X11/fonts/TTF; do + for dir in . /usr/src /usr/share/fonts/X11/misc /usr/share/fonts/truetype/ttf-dejavu /usr/share/fonts/dejavu /usr/share/fonts/truetype /usr/pkg/share/fonts/X11/TTF /usr/local/share/fonts/dejavu /usr/X11R6/lib/X11/fonts/TTF /usr/share/fonts/dejavu-sans-fonts; do if test -f "$dir/DejaVuSans.$ext"; then DJVU_FONT_SOURCE="$dir/DejaVuSans.$ext" break 2 From 2ffc14ba950209e049cebdcae9cdb7dafc046865 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Thu, 16 May 2024 22:22:58 +0300 Subject: [PATCH 143/402] types: Add missing casts in compile-time byteswaps Without them, e.g., 0x80LL on 64-bit target is 32-bit byte-swapped to 0xffffffff80000000 instead of correct 0x80000000. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- include/grub/types.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/grub/types.h b/include/grub/types.h index 064066e2e..45079bf65 100644 --- a/include/grub/types.h +++ b/include/grub/types.h @@ -196,8 +196,8 @@ static inline grub_uint16_t grub_swap_bytes16(grub_uint16_t _x) return (grub_uint16_t) ((_x << 8) | (_x >> 8)); } -#define grub_swap_bytes16_compile_time(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8)) -#define grub_swap_bytes32_compile_time(x) ((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) | (((x) & 0xff0000) >> 8) | (((x) & 0xff000000UL) >> 24)) +#define grub_swap_bytes16_compile_time(x) ((grub_uint16_t)((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8))) +#define grub_swap_bytes32_compile_time(x) ((grub_uint32_t)((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) | (((x) & 0xff0000) >> 8) | (((x) & 0xff000000UL) >> 24))) #define grub_swap_bytes64_compile_time(x) \ ({ \ grub_uint64_t _x = (x); \ From c464f1ec3486469cc47718a4de6730fefd1d9bf4 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Thu, 16 May 2024 22:27:41 +0300 Subject: [PATCH 144/402] fs/zfs/zfs: Mark vdev_zaps_v2 and head_errlog as supported We don't need any actual adjustments as we don't use the affected structures. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/fs/zfs/zfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index b5453e006..dbe4ea503 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -291,6 +291,8 @@ static const char *spa_feature_names[] = { "com.delphix:embedded_data", "com.delphix:extensible_dataset", "org.open-zfs:large_blocks", + "com.klarasystems:vdev_zaps_v2", + "com.delphix:head_errlog", NULL }; From fa36f6376087b81a4de05623a5cd871a8fa30b35 Mon Sep 17 00:00:00 2001 From: Avnish Chouhan Date: Thu, 23 May 2024 18:43:14 +0530 Subject: [PATCH 145/402] kern/ieee1275/init: Add IEEE 1275 Radix support for KVM on Power This patch adds support for Radix, Xive and Radix_gtse in Options vector5 which is required for KVM LPARs. KVM LPARs ONLY support Radix and not the Hash. Not enabling Radix on any PowerVM KVM LPARs will result in boot failure. Signed-off-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- grub-core/kern/ieee1275/init.c | 63 +++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index fb7d1a3ba..dfbd0b899 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -113,6 +113,16 @@ grub_addr_t grub_ieee1275_original_stack; #define DRC_INFO 0x40 #define BYTE22 (DY_MEM_V2 | DRC_INFO) +/* For ibm,arch-vec-5-platform-support. */ +#define XIVE_INDEX 0x17 +#define MMU_INDEX 0x18 +#define RADIX_GTSE_INDEX 0x1a +#define RADIX_ENABLED 0x40 +#define XIVE_ENABLED 0x40 +#define HASH_ENABLED 0x00 +#define MAX_SUPPORTED 0xC0 +#define RADIX_GTSE_ENABLED 0x40 + void grub_exit (void) { @@ -737,6 +747,10 @@ struct option_vector5 grub_uint32_t platform_facilities; grub_uint8_t sub_processors; grub_uint8_t byte22; + grub_uint8_t xive; + grub_uint8_t mmu; + grub_uint8_t hpt_ext; + grub_uint8_t radix_gtse; } GRUB_PACKED; struct pvr_entry @@ -775,6 +789,13 @@ grub_ieee1275_ibm_cas (void) { int rc; grub_ieee1275_ihandle_t root; + grub_uint8_t ibm_arch_platform_support[8]; + grub_ssize_t actual; + grub_uint8_t xive_support = 0; + grub_uint8_t mmu_support = 0; + grub_uint8_t radix_gtse_support = 0; + int i = 0; + int prop_len = 8; struct cas_args { struct grub_ieee1275_common_hdr common; @@ -783,6 +804,46 @@ grub_ieee1275_ibm_cas (void) grub_ieee1275_cell_t cas_addr; grub_ieee1275_cell_t result; } args; + + grub_ieee1275_get_integer_property (grub_ieee1275_chosen, + "ibm,arch-vec-5-platform-support", + (grub_uint32_t *) ibm_arch_platform_support, + sizeof (ibm_arch_platform_support), + &actual); + + for (i = 0; i < prop_len; i++) + { + switch (ibm_arch_platform_support[i]) + { + case XIVE_INDEX: + if (ibm_arch_platform_support[i + 1] & MAX_SUPPORTED) + xive_support = XIVE_ENABLED; + else + xive_support = 0; + break; + + case MMU_INDEX: + if (ibm_arch_platform_support[i + 1] & MAX_SUPPORTED) + mmu_support = RADIX_ENABLED; + else + mmu_support = HASH_ENABLED; + break; + + case RADIX_GTSE_INDEX: + if (mmu_support == RADIX_ENABLED) + radix_gtse_support = ibm_arch_platform_support[i + 1] & RADIX_GTSE_ENABLED; + else + radix_gtse_support = 0; + break; + + default: + /* Ignoring the other indexes of ibm,arch-vec-5-platform-support. */ + break; + } + /* Skipping the property value. */ + i++; + } + struct cas_vector vector = { .pvr_list = { { 0x00000000, 0xffffffff } }, /* any processor */ @@ -799,7 +860,7 @@ grub_ieee1275_ibm_cas (void) .vec4 = 0x0001, /* set required minimum capacity % to the lowest value */ .vec5_size = 1 + sizeof (struct option_vector5) - 2, .vec5 = { - 0, BYTE2, 0, CMO, ASSOCIATIVITY, BIN_OPTS, 0, 0, MAX_CPU, 0, 0, PLATFORM_FACILITIES, SUB_PROCESSORS, BYTE22 + 0, BYTE2, 0, CMO, ASSOCIATIVITY, BIN_OPTS, 0, 0, MAX_CPU, 0, 0, PLATFORM_FACILITIES, SUB_PROCESSORS, BYTE22, xive_support, mmu_support, 0, radix_gtse_support } }; From 0b4d01794a08efc6f6de88c5a08ff81bb52122d4 Mon Sep 17 00:00:00 2001 From: Tianjia Zhang Date: Mon, 27 May 2024 20:42:04 +0800 Subject: [PATCH 146/402] util/grub-mkpasswd-pbkdf2: Simplify the main function implementation Allocate memory if needed, while saving the corresponding release operation, reducing the amount of code and code complexity. Signed-off-by: Tianjia Zhang Reviewed-by: Daniel Kiper --- util/grub-mkpasswd-pbkdf2.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/util/grub-mkpasswd-pbkdf2.c b/util/grub-mkpasswd-pbkdf2.c index 29c68fbf5..51e706510 100644 --- a/util/grub-mkpasswd-pbkdf2.c +++ b/util/grub-mkpasswd-pbkdf2.c @@ -133,34 +133,24 @@ main (int argc, char *argv[]) exit(1); } - buf = xmalloc (arguments.buflen); - salt = xmalloc (arguments.saltlen); - printf ("%s", _("Enter password: ")); if (!grub_password_get (pass1, GRUB_AUTH_MAX_PASSLEN)) - { - free (buf); - free (salt); - grub_util_error ("%s", _("failure to read password")); - } + grub_util_error ("%s", _("failure to read password")); printf ("%s", _("Reenter password: ")); if (!grub_password_get (pass2, GRUB_AUTH_MAX_PASSLEN)) - { - free (buf); - free (salt); - grub_util_error ("%s", _("failure to read password")); - } + grub_util_error ("%s", _("failure to read password")); if (strcmp (pass1, pass2) != 0) { memset (pass1, 0, sizeof (pass1)); memset (pass2, 0, sizeof (pass2)); - free (buf); - free (salt); grub_util_error ("%s", _("passwords don't match")); } memset (pass2, 0, sizeof (pass2)); + buf = xmalloc (arguments.buflen); + salt = xmalloc (arguments.saltlen); + if (grub_get_random (salt, arguments.saltlen)) { memset (pass1, 0, sizeof (pass1)); From 86df79275d065d87f4de5c97e456973e8b4a649c Mon Sep 17 00:00:00 2001 From: Hector Cao Date: Mon, 3 Jun 2024 23:36:25 +0200 Subject: [PATCH 147/402] commands/efi/tpm: Re-enable measurements on confidential computing platforms The measurements for confidential computing has been introduced in the commit 4c76565b6 (efi/tpm: Add EFI_CC_MEASUREMENT_PROTOCOL support). Recently the patch 30708dfe3 (tpm: Disable the tpm verifier if the TPM device is not present) has been introduced to optimize the memory usage when a TPM device is not available on platforms. This fix prevents the tpm module to be loaded on confidential computing platforms, e.g. Intel machines with TDX enabled, where the TPM device is not available. In this patch, we propose to load the tpm module for this use case by generalizing the tpm feature detection in order to cover CC platforms. Basically, we do it by detecting the availability of the EFI_CC_MEASUREMENT_PROTOCOL EFI protocol. Fixes: https://savannah.gnu.org/bugs/?65821 Fixes: 30708dfe3 (tpm: Disable the tpm verifier if the TPM device is not present) Signed-off-by: Hector Cao Reviewed-by: Daniel Kiper Reviewed-by: Kuppuswamy Sathyanarayanan --- grub-core/commands/efi/tpm.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/grub-core/commands/efi/tpm.c b/grub-core/commands/efi/tpm.c index f250c30db..cbac69866 100644 --- a/grub-core/commands/efi/tpm.c +++ b/grub-core/commands/efi/tpm.c @@ -292,6 +292,15 @@ grub_tpm_present (void) { grub_efi_handle_t tpm_handle; grub_efi_uint8_t protocol_version; + grub_efi_cc_protocol_t *cc; + + /* + * When confidential computing measurement protocol is enabled + * we assume the TPM is present. + */ + cc = grub_efi_locate_protocol (&cc_measurement_guid, NULL); + if (cc != NULL) + return 1; if (!grub_tpm_handle_find (&tpm_handle, &protocol_version)) return 0; From d35ff22516b161f6d472f7f5371a89597b072d04 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Mon, 6 May 2024 10:34:22 +0800 Subject: [PATCH 148/402] net/drivers/ieee1275/ofnet: Remove 200 ms timeout in get_card_packet() to reduce input latency When GRUB image is netbooted on ppc64le, the keyboard input exhibits significant latency, reports even say that characters are processed about once per second. This issue makes interactively trying to debug a ppc64le config very difficult. It seems that the latency is largely caused by a 200 ms timeout in the idle event loop, during which the network card interface is consistently polled for incoming packets. Often, no packets arrive during this period, so the timeout nearly always expires, which blocks the response to key inputs. Furthermore, this 200 ms timeout might not need to be enforced at this basic layer, considering that GRUB performs synchronous reads and its timeout management is actually handled by higher layers, not directly in the card instance. Additionally, the idle polling, which reacts to unsolicited packets like ICMP and SLAAC, would be fine at a less frequent polling interval, rather than needing a timeout for receiving a response. For these reasons, we believe the timeout in get_card_packet() should be effectively removed. According to test results, the delay has disappeared, and it is now much easier to use interactively. Signed-Off-by: Michael Chang Tested-by: Tony Jones Reviewed-by: Daniel Kiper --- grub-core/net/drivers/ieee1275/ofnet.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/grub-core/net/drivers/ieee1275/ofnet.c b/grub-core/net/drivers/ieee1275/ofnet.c index 78f03df8e..3bf48b3f0 100644 --- a/grub-core/net/drivers/ieee1275/ofnet.c +++ b/grub-core/net/drivers/ieee1275/ofnet.c @@ -82,15 +82,11 @@ get_card_packet (struct grub_net_card *dev) grub_ssize_t actual; int rc; struct grub_ofnetcard_data *data = dev->data; - grub_uint64_t start_time; struct grub_net_buff *nb; - start_time = grub_get_time_ms (); - do - rc = grub_ieee1275_read (data->handle, dev->rcvbuf, dev->rcvbufsize, &actual); - while ((actual <= 0 || rc < 0) && (grub_get_time_ms () - start_time < 200)); + rc = grub_ieee1275_read (data->handle, dev->rcvbuf, dev->rcvbufsize, &actual); - if (actual <= 0) + if (actual <= 0 || rc < 0) return NULL; nb = grub_netbuff_alloc (actual + 2); From 8abec8e1538a7ea885713b4c8b5ad18f4b401b33 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Fri, 17 May 2024 10:53:27 +0300 Subject: [PATCH 149/402] loader/i386/multiboot_mbi: Fix handling of errors in broken aout-kludge Current code in some codepaths neither discards nor reports errors. Properly surface the error. While on it split 2 cases of unrelated variables both named err. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/loader/i386/multiboot_mbi.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/grub-core/loader/i386/multiboot_mbi.c b/grub-core/loader/i386/multiboot_mbi.c index fae5d6fb8..bdaf67ad0 100644 --- a/grub-core/loader/i386/multiboot_mbi.c +++ b/grub-core/loader/i386/multiboot_mbi.c @@ -69,7 +69,6 @@ static grub_err_t load_kernel (grub_file_t file, const char *filename, char *buffer, struct multiboot_header *header) { - grub_err_t err; mbi_load_data_t mld; mld.file = file; @@ -81,12 +80,15 @@ load_kernel (grub_file_t file, const char *filename, if (grub_multiboot_quirks & GRUB_MULTIBOOT_QUIRK_BAD_KLUDGE) { - err = grub_multiboot_load_elf (&mld); + grub_err_t err = grub_multiboot_load_elf (&mld); + if (err == GRUB_ERR_NONE) { return GRUB_ERR_NONE; } if (err == GRUB_ERR_UNKNOWN_OS && (header->flags & MULTIBOOT_AOUT_KLUDGE)) - grub_errno = GRUB_ERR_NONE; + grub_errno = err = GRUB_ERR_NONE; + if (err != GRUB_ERR_NONE) + return err; } if (header->flags & MULTIBOOT_AOUT_KLUDGE) { @@ -97,6 +99,7 @@ load_kernel (grub_file_t file, const char *filename, grub_size_t code_size; void *source; grub_relocator_chunk_t ch; + grub_err_t err; if (header->bss_end_addr) code_size = (header->bss_end_addr - header->load_addr); From 13b315c0a51e91d5b681f6b8a51a2bc475d08a3f Mon Sep 17 00:00:00 2001 From: Udo Steinberg Date: Fri, 7 Jun 2024 23:44:43 +0200 Subject: [PATCH 150/402] term/ns8250-spcr: Add one more 16550 debug type Type 0x01 was introduced with the ACPI DBGP table and type 0x12 was introduced with the ACPI DBG2 table. Type 0x12 is used by the ACPI SPCR table on recent AWS bare-metal instances (c6i/c7i). Also give each debug type a proper name. Signed-off-by: Udo Steinberg Reviewed-by: Glenn Washburn Reviewed-by: Daniel Kiper --- grub-core/term/ns8250-spcr.c | 3 ++- include/grub/acpi.h | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/grub-core/term/ns8250-spcr.c b/grub-core/term/ns8250-spcr.c index d52b52c26..4efaaf768 100644 --- a/grub-core/term/ns8250-spcr.c +++ b/grub-core/term/ns8250-spcr.c @@ -38,7 +38,8 @@ grub_ns8250_spcr_init (void) grub_dprintf ("serial", "SPCR table revision %d < 2, continuing anyway\n", (int) spcr->hdr.revision); if (spcr->intf_type != GRUB_ACPI_SPCR_INTF_TYPE_16550 && - spcr->intf_type != GRUB_ACPI_SPCR_INTF_TYPE_16550X) + spcr->intf_type != GRUB_ACPI_SPCR_INTF_TYPE_16550_DBGP && + spcr->intf_type != GRUB_ACPI_SPCR_INTF_TYPE_16550_DBG2) return NULL; /* For now, we only support byte accesses. */ if (spcr->base_addr.access_size != GRUB_ACPI_GENADDR_SIZE_BYTE && diff --git a/include/grub/acpi.h b/include/grub/acpi.h index f2862da74..1046f22d7 100644 --- a/include/grub/acpi.h +++ b/include/grub/acpi.h @@ -200,8 +200,9 @@ struct grub_acpi_genaddr { struct grub_acpi_spcr { struct grub_acpi_table_header hdr; grub_uint8_t intf_type; -#define GRUB_ACPI_SPCR_INTF_TYPE_16550 0x00 -#define GRUB_ACPI_SPCR_INTF_TYPE_16550X 0x01 +#define GRUB_ACPI_SPCR_INTF_TYPE_16550 0x00 +#define GRUB_ACPI_SPCR_INTF_TYPE_16550_DBGP 0x01 +#define GRUB_ACPI_SPCR_INTF_TYPE_16550_DBG2 0x12 grub_uint8_t reserved_0[3]; struct grub_acpi_genaddr base_addr; grub_uint8_t interrupt_type; From b8d29f1146eb7ad7a3bde3b39249f4c8673537b9 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Sat, 8 Jun 2024 17:44:15 -0500 Subject: [PATCH 151/402] configure: Add Debian/Ubuntu DejaVu font path Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 99aae8700..d4a14bf93 100644 --- a/configure.ac +++ b/configure.ac @@ -1847,7 +1847,7 @@ if test "x$with_dejavufont" = x; then # search in well-known directories if test x"$starfield_excuse" = x; then for ext in pcf pcf.gz bdf bdf.gz ttf ttf.gz; do - for dir in . /usr/src /usr/share/fonts/X11/misc /usr/share/fonts/truetype/ttf-dejavu /usr/share/fonts/dejavu /usr/share/fonts/truetype /usr/pkg/share/fonts/X11/TTF /usr/local/share/fonts/dejavu /usr/X11R6/lib/X11/fonts/TTF /usr/share/fonts/dejavu-sans-fonts; do + for dir in . /usr/src /usr/share/fonts/X11/misc /usr/share/fonts/truetype/ttf-dejavu /usr/share/fonts/dejavu /usr/share/fonts/truetype /usr/pkg/share/fonts/X11/TTF /usr/local/share/fonts/dejavu /usr/X11R6/lib/X11/fonts/TTF /usr/share/fonts/dejavu-sans-fonts /usr/share/fonts/truetype/dejavu; do if test -f "$dir/DejaVuSans.$ext"; then DJVU_FONT_SOURCE="$dir/DejaVuSans.$ext" break 2 From 88a7e64c2c38ff5d42098427e87563bceae3a4ba Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Sat, 8 Jun 2024 21:22:31 -0500 Subject: [PATCH 152/402] tests/util/grub-shell: Print gdbinfo if on EFI platform Allow using GDB to debug a failing QEMU test. This output does not cause issues for tests because it happens before the trim line, and so will be ignored. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- tests/util/grub-shell.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/util/grub-shell.in b/tests/util/grub-shell.in index 496e1bab3..3113cc3a2 100644 --- a/tests/util/grub-shell.in +++ b/tests/util/grub-shell.in @@ -360,6 +360,9 @@ halt_cmd=${halt_cmd} export halt_cmd enable_progress_indicator=0 export enable_progress_indicator +if [ "${grub_modinfo_platform}" == efi ]; then + gdbinfo +fi EOF From d2fc9dfcd1ca6fa7e8bd281b1b2d36dc125ecd20 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Sat, 8 Jun 2024 21:42:34 -0500 Subject: [PATCH 153/402] tests/util/grub-shell: Use pflash instead of -bios to load UEFI firmware According to the OVMF whitepaper [1]: IMPORTANT: Never pass OVMF.fd to qemu with the -bios option. That option maps the firmware image as ROM into the guest's address space, and forces OVMF to emulate non-volatile variables with a fallback driver that is bound to have insufficient and confusing semantics. Use the pflash interface instead. Currently the unified firmware file is used, which contains both firmware code and variable sections. By enabling snapshot on the pflash device, the firmware can be loaded in such a way that variables can be written to without writing to the backing file. Since pflash does no searching for firmware paths that are not absolute, unlike the -bios option, also make firmware paths absolute. Additionally, update the previous firmware paths or file names that did not correspond to ones installed by Debian. Use the q35 machine, instead of the default i440fx, for i386-efi because the default machine type does not emulate a flash device, which is now needed to load the firmware. [1] http://www.linux-kvm.org/downloads/lersek/ovmf-whitepaper-c770f8c.txt Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- tests/util/grub-shell.in | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/util/grub-shell.in b/tests/util/grub-shell.in index 3113cc3a2..89b9d4205 100644 --- a/tests/util/grub-shell.in +++ b/tests/util/grub-shell.in @@ -181,21 +181,26 @@ case "${grub_modinfo_target_cpu}-${grub_modinfo_platform}" in boot=cd console=console trim=1 - qemuopts="-bios OVMF-ia32.fd $qemuopts" + pflash=/usr/share/qemu/OVMF32.fd + qemuopts="-drive if=pflash,format=raw,unit=0,snapshot=on,file=$pflash $qemuopts" + qemuopts="-machine q35 $qemuopts" ;; x86_64-efi) qemu=qemu-system-x86_64 boot=cd console=console trim=1 - qemuopts="-bios OVMF.fd $qemuopts" + pflash=/usr/share/qemu/OVMF.fd + qemuopts="-drive if=pflash,format=raw,unit=0,snapshot=on,file=$pflash $qemuopts" ;; arm64-efi) qemu=qemu-system-aarch64 boot=hd console=console trim=1 - qemuopts="-machine virt -cpu cortex-a57 -bios /usr/share/qemu-efi/QEMU_EFI.fd $qemuopts" + pflash=/usr/share/qemu-efi-aarch64/QEMU_EFI.fd + qemuopts="-drive if=pflash,format=raw,unit=0,snapshot=on,file=$pflash $qemuopts" + qemuopts="-machine virt -cpu cortex-a57 $qemuopts" disk="device virtio-blk-device,drive=hd1 -drive if=none,id=hd1,file=" serial_port= ;; @@ -204,7 +209,9 @@ case "${grub_modinfo_target_cpu}-${grub_modinfo_platform}" in boot=hd console=console trim=1 - qemuopts="-machine virt -bios /usr/share/ovmf-arm/QEMU_EFI.fd $qemuopts" + pflash=/usr/share/AAVMF/AAVMF32.fd + qemuopts="-drive if=pflash,format=raw,unit=0,snapshot=on,file=$pflash $qemuopts" + qemuopts="-machine virt $qemuopts" disk="device virtio-blk-device,drive=hd1 -drive if=none,id=hd1,file=" serial_port=efi0 ;; From c22e052fe2faa177c0f8316cc9d72ed6e2496e36 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Sat, 8 Jun 2024 21:42:35 -0500 Subject: [PATCH 154/402] tests/util/grub-shell: Add flexibility in QEMU firmware handling First look for firmware files in the source directory and then, if not found, look for them in locations where Debian installs them. Prefer to use the unified firmware file and, if not found, use the pflash firmware files split in to code and variables. By looking for files in the source directory first, system firmware files can be overridden and it can be ensured that the tests can be run regardless of the distro or where the system firmware files are stored. If no firmware files are found, print an error message and exit with error. If a firmware VARS file is found, use it with snapshot mode enabled, which makes the VARS writable to the virtual machine, but does not write back the changes to the file. This allows using the readonly system VARS file without copying it or using it in readonly mode, which causes the ARM machine to fail. This also gives tests effectively their own ephemeral VARS file that can be written to without causing side-effects for other tests. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- tests/util/grub-shell.in | 104 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 96 insertions(+), 8 deletions(-) diff --git a/tests/util/grub-shell.in b/tests/util/grub-shell.in index 89b9d4205..ae5f711fe 100644 --- a/tests/util/grub-shell.in +++ b/tests/util/grub-shell.in @@ -181,8 +181,30 @@ case "${grub_modinfo_target_cpu}-${grub_modinfo_platform}" in boot=cd console=console trim=1 - pflash=/usr/share/qemu/OVMF32.fd - qemuopts="-drive if=pflash,format=raw,unit=0,snapshot=on,file=$pflash $qemuopts" + pflash=${srcdir}/OVMF32.fd + pflash_code=${srcdir}/OVMF32_CODE.fd + pflash_vars=${srcdir}/OVMF32_VARS.fd + if [ -f "$pflash" ]; then + qemuopts="-drive if=pflash,format=raw,unit=0,snapshot=on,file=$pflash $qemuopts" + elif [ -f "$pflash_code" ]; then + qemuopts="-drive if=pflash,format=raw,unit=0,readonly=on,file=$pflash_code $qemuopts" + if [ -f "$pflash_vars" ]; then + qemuopts="-drive if=pflash,format=raw,unit=1,snapshot=on,file=$pflash_vars $qemuopts" + fi + else + pflash=/usr/share/qemu/OVMF32.fd + pflash_code=/usr/share/OVMF/OVMF32_CODE_4M.secboot.fd + pflash_vars=/usr/share/OVMF/OVMF32_VARS_4M.fd + if [ -f "$pflash" ]; then + qemuopts="-drive if=pflash,format=raw,unit=0,snapshot=on,file=$pflash $qemuopts" + elif [ -f "$pflash_code" ]; then + qemuopts="-drive if=pflash,format=raw,unit=0,readonly=on,file=$pflash_code $qemuopts" + qemuopts="-drive if=pflash,format=raw,unit=1,snapshot=on,file=$pflash_vars $qemuopts" + else + echo "Firmware not found, please install either the appropriate Debian package or an appropriately named copy in the source directory." >&2 + exit 1 + fi + fi qemuopts="-machine q35 $qemuopts" ;; x86_64-efi) @@ -190,16 +212,60 @@ case "${grub_modinfo_target_cpu}-${grub_modinfo_platform}" in boot=cd console=console trim=1 - pflash=/usr/share/qemu/OVMF.fd - qemuopts="-drive if=pflash,format=raw,unit=0,snapshot=on,file=$pflash $qemuopts" + pflash=${srcdir}/OVMF.fd + pflash_code=${srcdir}/OVMF_CODE.fd + pflash_vars=${srcdir}/OVMF_VARS.fd + if [ -f "$pflash" ]; then + qemuopts="-drive if=pflash,format=raw,unit=0,snapshot=on,file=$pflash $qemuopts" + elif [ -f "$pflash_code" ]; then + qemuopts="-drive if=pflash,format=raw,unit=0,readonly=on,file=$pflash_code $qemuopts" + if [ -f "$pflash_vars" ]; then + qemuopts="-drive if=pflash,format=raw,unit=1,snapshot=on,file=$pflash_vars $qemuopts" + fi + else + pflash=/usr/share/qemu/OVMF.fd + pflash_code=/usr/share/OVMF/OVMF_CODE.fd + pflash_vars=/usr/share/OVMF/OVMF_VARS.fd + if [ -f "$pflash" ]; then + qemuopts="-drive if=pflash,format=raw,unit=0,snapshot=on,file=$pflash $qemuopts" + elif [ -f "$pflash_code" ]; then + qemuopts="-drive if=pflash,format=raw,unit=0,readonly=on,file=$pflash_code $qemuopts" + qemuopts="-drive if=pflash,format=raw,unit=1,snapshot=on,file=$pflash_vars $qemuopts" + else + echo "Firmware not found, please install either the appropriate Debian package or an appropriately named copy in the source directory." >&2 + exit 1 + fi + fi ;; arm64-efi) qemu=qemu-system-aarch64 boot=hd console=console trim=1 - pflash=/usr/share/qemu-efi-aarch64/QEMU_EFI.fd - qemuopts="-drive if=pflash,format=raw,unit=0,snapshot=on,file=$pflash $qemuopts" + pflash=${srcdir}/AAVMF.fd + pflash_code=${srcdir}/AAVMF_CODE.fd + pflash_vars=${srcdir}/AAVMF_VARS.fd + if [ -f "$pflash" ]; then + qemuopts="-drive if=pflash,format=raw,unit=0,snapshot=on,file=$pflash $qemuopts" + elif [ -f "$pflash_code" ]; then + qemuopts="-drive if=pflash,format=raw,unit=0,readonly=on,file=$pflash_code $qemuopts" + if [ -f "$pflash_vars" ]; then + qemuopts="-drive if=pflash,format=raw,unit=1,snapshot=on,file=$pflash_vars $qemuopts" + fi + else + pflash=/usr/share/qemu-efi-aarch64/QEMU_EFI.fd + pflash_code=/usr/share/AAVMF/AAVMF_CODE.fd + pflash_vars=/usr/share/AAVMF/AAVMF_VARS.fd + if [ -f "$pflash" ]; then + qemuopts="-drive if=pflash,format=raw,unit=0,snapshot=on,file=$pflash $qemuopts" + elif [ -f "$pflash_code" ]; then + qemuopts="-drive if=pflash,format=raw,unit=0,readonly=on,file=$pflash_code $qemuopts" + qemuopts="-drive if=pflash,format=raw,unit=1,snapshot=on,file=$pflash_vars $qemuopts" + else + echo "Firmware not found, please install either the appropriate Debian package or an appropriately named copy in the source directory." >&2 + exit 1 + fi + fi qemuopts="-machine virt -cpu cortex-a57 $qemuopts" disk="device virtio-blk-device,drive=hd1 -drive if=none,id=hd1,file=" serial_port= @@ -209,8 +275,30 @@ case "${grub_modinfo_target_cpu}-${grub_modinfo_platform}" in boot=hd console=console trim=1 - pflash=/usr/share/AAVMF/AAVMF32.fd - qemuopts="-drive if=pflash,format=raw,unit=0,snapshot=on,file=$pflash $qemuopts" + pflash=${srcdir}/AAVMF32.fd + pflash_code=${srcdir}/AAVMF32_CODE.fd + pflash_vars=${srcdir}/AAVMF32_VARS.fd + if [ -f "$pflash" ]; then + qemuopts="-drive if=pflash,format=raw,unit=0,snapshot=on,file=$pflash $qemuopts" + elif [ -f "$pflash_code" ]; then + qemuopts="-drive if=pflash,format=raw,unit=0,readonly=on,file=$pflash_code $qemuopts" + if [ -f "$pflash_vars" ]; then + qemuopts="-drive if=pflash,format=raw,unit=1,snapshot=on,file=$pflash_vars $qemuopts" + fi + else + pflash=/usr/share/AAVMF/AAVMF32.fd + pflash_code=/usr/share/AAVMF/AAVMF32_CODE.fd + pflash_vars=/usr/share/AAVMF/AAVMF32_VARS.fd + if [ -f "$pflash" ]; then + qemuopts="-drive if=pflash,format=raw,unit=0,snapshot=on,file=$pflash $qemuopts" + elif [ -f "$pflash_code" ]; then + qemuopts="-drive if=pflash,format=raw,unit=0,readonly=on,file=$pflash_code $qemuopts" + qemuopts="-drive if=pflash,format=raw,unit=1,snapshot=on,file=$pflash_vars $qemuopts" + else + echo "Firmware not found, please install either the appropriate Debian package or an appropriately named copy in the source directory." >&2 + exit 1 + fi + fi qemuopts="-machine virt $qemuopts" disk="device virtio-blk-device,drive=hd1 -drive if=none,id=hd1,file=" serial_port=efi0 From c1ee4da6a9e58a292cc479702439115f837b08fd Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Sat, 8 Jun 2024 23:22:05 -0500 Subject: [PATCH 155/402] tests/util/grub-shell-luks-tester: Fix detached header test getting wrong header path When $detached_header was set 1, $luksdiskfile was set to the LUKS header file path with "${detached_header:-$luksfile}" appended, which evaluates to "1". Fix this by using two statements to set $luksdiskfile. The first sets it to the header file if $detached_header is set, otherwise leave it unset. The second statement sets it to itself if it is already set, otherwise it is set to $luksfile. Fixes: a7b540e6e (tests: Add cryptomount functional test) Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- tests/util/grub-shell-luks-tester.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/util/grub-shell-luks-tester.in b/tests/util/grub-shell-luks-tester.in index 8926cbc96..b2a8a91b4 100644 --- a/tests/util/grub-shell-luks-tester.in +++ b/tests/util/grub-shell-luks-tester.in @@ -162,6 +162,8 @@ get_random_bytes() { lukstestdir="`mktemp -d "${TMPDIR:-/tmp}/$(basename "$0").XXXXXXXXXX"`" || exit 20 luksfile=$lukstestdir/luks.disk lukshdrfile=$lukstestdir/luks.header +luksdiskfile=${detached_header:+$lukshdrfile} +luksdiskfile=${luksdiskfile:-$luksfile} lukskeyfile=$lukstestdir/luks.key vfile=$lukstestdir/mnt/test.verify vtext="TEST VERIFIED" @@ -193,7 +195,7 @@ truncate -s $disksize $luksfile || exit 21 cryptsetup luksFormat -q $csopts $luksfile $lukskeyfile || exit 22 # Run any cryptsetup scripts -export luksdiskfile=${detached_header:+$lukshdrfile}${detached_header:-$luksfile} +export luksdiskfile export lukskeyfile for csscript in $csscripts; do [ -f "$csscript" ] && . $csscript From d41c64811d6859c63c1d46da326771a2d32d2d2f Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Sat, 8 Jun 2024 23:42:43 -0500 Subject: [PATCH 156/402] tests: Switch to requiring exfatprogs from exfat-utils The current Debian stable, now 12, has dropped the exfat-utils package that the exfat filesystem test requires to run. There is an exfatprogs package that replaces exfat-utils, though it is not a drop-in replacement because mkfs.exfat has differing command line option names. Note, that we're not yet switching to using the exfat kernel module because this allows the testings on kernels that do not have the module. Update mkfs.exfat usage to adhere to the different exfatprogs usage. Also, the exfatprogs mkfs.exfat, following the exfat specification more closely, only allows a maximum of 22 bytes of UTF-16 characters in the volume label compared to 30 bytes from exfat-utils. So the exfat label test is updated accordingly. Update documentation to note that exfatprogs is now needed and also exfat-fuse, which is needed do the fuse mount. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- INSTALL | 6 +++--- tests/util/grub-fs-tester.in | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/INSTALL b/INSTALL index 84030c9f4..6b04e3016 100644 --- a/INSTALL +++ b/INSTALL @@ -83,9 +83,9 @@ Prerequisites for make-check: exfat FUSE filesystem * 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, exfat-utils, f2fs-tools, - genromfs, hfsprogs, jfsutils, nilfs-tools, ntfs-3g, reiserfsprogs, - squashfs-tools, reiserfsprogs, udftools, xfsprogs, zfs-fuse + - btrfs-progs, dosfstools, e2fsprogs, erofs-utils, exfatprogs, exfat-fuse, + f2fs-tools, genromfs, hfsprogs, jfsutils, nilfs-tools, ntfs-3g, + reiserfsprogs, squashfs-tools, reiserfsprogs, udftools, xfsprogs, zfs-fuse - exfat-fuse, if not using the exfat kernel module - gzip, lzop, xz-utils - attr, cpio, g++, gawk, parted, recode, tar, util-linux diff --git a/tests/util/grub-fs-tester.in b/tests/util/grub-fs-tester.in index df5dc7542..d0ba85324 100644 --- a/tests/util/grub-fs-tester.in +++ b/tests/util/grub-fs-tester.in @@ -362,9 +362,9 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do x"f2fs") FSLABEL="grub_;/testĂ©äŒä“ä•Đșоротiurewfceniuewruewnuuireurevueurnievrewfnerfcnevirivinrewvnirewnivrewiuvcrewvnuewvrrrewniuerwreiuviurewiuviurewnuvewnvrenurnunuvrevuurerejiremvreijnvvcreivire nverivnreivrevnureiorfnfrvoeoiroireoireoifrefoieroifoirvcreivire nverivnreivrevnureiorfnfrvoeoiroireoireoifrefoieroifoircreivire nverivnreivrevnureiorfnfrvoeoiroireoireoifrefoieroifoireoifoiq";; - # FS LIMITATION: exfat is at most 15 UTF-16 chars + # FS LIMITATION: exfat is at most 22 bytes of UTF-16 chars x"exfat") - FSLABEL="géт ;/𯩛𯩝😁Đșор";; + FSLABEL="éт ;/𯩛𯩝😁";; # FS LIMITATION: ntfs label is at most ?? UTF-16 chars x"ntfs"*) FSLABEL="grub_;/testéтi u𯩛😁𯩙Đșороrewfceniuewruevrewnuuireurevueurnievrewfnerfcnevirivinrewvniwnivrewiuvcrewvnuewvrrrewniureifiuewifjiww";; @@ -710,7 +710,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do MOUNTFS="btrfs" ;; x"exfat") - "mkfs.$fs" -s $((BLKSIZE/512)) -n "$FSLABEL" "${MOUNTDEVICE}" + "mkfs.$fs" -c $SECSIZE -L "$FSLABEL" "${MOUNTDEVICE}" MOUNTOPTS="iocharset=utf8," MOUNTFS="exfat-fuse";; x"minix") From b990df0bef9eb9f8ed4a111dfb0cdf6f88ff8829 Mon Sep 17 00:00:00 2001 From: Yifan Zhao Date: Wed, 12 Jun 2024 15:28:41 +0800 Subject: [PATCH 157/402] tests/util/grub-fs-tester: Fix EROFS label tests in grub-fs-tester mkfs.erofs with version < 1.6 does not support the -L option. Let's detect the version of mkfs.erofs and skip the label tests if it is not supported. Suggested-by: Glenn Washburn Signed-off-by: Yifan Zhao Reviewed-by: Daniel Kiper --- tests/util/grub-fs-tester.in | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/tests/util/grub-fs-tester.in b/tests/util/grub-fs-tester.in index d0ba85324..2f5e9bef6 100644 --- a/tests/util/grub-fs-tester.in +++ b/tests/util/grub-fs-tester.in @@ -595,6 +595,12 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do x"cpio_"* | x"tarfs" | x"squash4_"* | x"minix" | x"minix2" \ | x"minix3" | xreiserfs_old) NOFSLABEL=y;; + x"erofs_"*) + MKFS_EROFS_VERSION=$(mkfs.erofs -V 2>/dev/null | tr ' ' '\n' | grep '^[0-9]') + # 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 + NOFSLABEL=y + fi esac PDIRCOMPNUM=210 @@ -1121,16 +1127,34 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do echo mksquashfs "$MASTER" "${FSIMAGEP}0.img" -always-use-fragments -comp "${fs/squash4_/}" -b $BLKSIZE mksquashfs "$MASTER" "${FSIMAGEP}0.img" -always-use-fragments -comp "${fs/squash4_/}" -b $BLKSIZE ;; x"erofs_compact") - echo mkfs.erofs -Eforce-inode-compact -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW" - mkfs.erofs -Eforce-inode-compact -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW" + cmd="mkfs.erofs" + if [ x"$NOFSLABEL" != xy ]; then + cmd="$cmd -L \"$FSLABEL\"" + fi + cmd="$cmd -Eforce-inode-compact ${FSIMAGEP}0.img $MNTPOINTRW" + echo $cmd + eval $cmd + unset cmd ;; x"erofs_extended") - echo mkfs.erofs -Eforce-inode-extended -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW" - mkfs.erofs -Eforce-inode-extended -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW" + cmd="mkfs.erofs" + if [ x"$NOFSLABEL" != xy ]; then + cmd="$cmd -L \"$FSLABEL\"" + fi + cmd="$cmd -Eforce-inode-extended ${FSIMAGEP}0.img $MNTPOINTRW" + echo $cmd + eval $cmd + unset cmd ;; x"erofs_chunk") - echo mkfs.erofs --chunksize=1048576 -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW" - mkfs.erofs --chunksize=1048576 -L "$FSLABEL" "${FSIMAGEP}0.img" "$MNTPOINTRW" + cmd="mkfs.erofs" + if [ x"$NOFSLABEL" != xy ]; then + cmd="$cmd -L \"$FSLABEL\"" + fi + cmd="$cmd --chunksize=1048576 ${FSIMAGEP}0.img $MNTPOINTRW" + echo $cmd + eval $cmd + unset cmd ;; x"bfs") sleep 1 From dc0a3a27d67a6b1639d155cc69e8537077af1047 Mon Sep 17 00:00:00 2001 From: Mate Kukri Date: Wed, 12 Jun 2024 16:10:49 +0100 Subject: [PATCH 158/402] kern/efi/mm: Change grub_efi_mm_add_regions() to keep track of map allocation size If the map was too big for the initial allocation, it was freed and replaced with a bigger one, but the free call still used the hard-coded size. Seems like this wasn't hit for a long time, because most firmware maps fit into 12K. This bug was triggered on Project Mu firmware with a big memory map, and results in the heap getting trashed and the firmware ASSERTING on corrupted heap guard values when GRUB exits. Signed-off-by: Mate Kukri Reviewed-by: Daniel Kiper --- grub-core/kern/efi/mm.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c index 6a6fba891..739229e7b 100644 --- a/grub-core/kern/efi/mm.c +++ b/grub-core/kern/efi/mm.c @@ -574,13 +574,15 @@ grub_efi_mm_add_regions (grub_size_t required_bytes, unsigned int flags) grub_efi_memory_descriptor_t *memory_map_end; grub_efi_memory_descriptor_t *filtered_memory_map; grub_efi_memory_descriptor_t *filtered_memory_map_end; + grub_efi_uintn_t alloc_size; grub_efi_uintn_t map_size; grub_efi_uintn_t desc_size; grub_err_t err; int mm_status; /* Prepare a memory region to store two memory maps. */ - memory_map = grub_efi_allocate_any_pages (2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE)); + alloc_size = 2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE); + memory_map = grub_efi_allocate_any_pages (alloc_size); if (! memory_map) return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory for memory map"); @@ -591,14 +593,13 @@ grub_efi_mm_add_regions (grub_size_t required_bytes, unsigned int flags) if (mm_status == 0) { - grub_efi_free_pages - ((grub_efi_physical_address_t) ((grub_addr_t) memory_map), - 2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE)); + grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t) memory_map, alloc_size); /* Freeing/allocating operations may increase memory map size. */ map_size += desc_size * 32; - memory_map = grub_efi_allocate_any_pages (2 * BYTES_TO_PAGES (map_size)); + alloc_size = 2 * BYTES_TO_PAGES (map_size); + memory_map = grub_efi_allocate_any_pages (alloc_size); if (! memory_map) return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory for new memory map"); @@ -642,8 +643,7 @@ grub_efi_mm_add_regions (grub_size_t required_bytes, unsigned int flags) #endif /* Release the memory maps. */ - grub_efi_free_pages ((grub_addr_t) memory_map, - 2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE)); + grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t) memory_map, alloc_size); return GRUB_ERR_NONE; } From 61f1d0a612a01c2f48f718ff04590ff481e028f8 Mon Sep 17 00:00:00 2001 From: Mate Kukri Date: Wed, 12 Jun 2024 16:10:50 +0100 Subject: [PATCH 159/402] kern/efi/mm: Change grub_efi_allocate_pages_real() to call semantically correct free function If the firmware happens to return 0 as an address of allocated pages, grub_efi_allocate_pages_real() tries to allocate a new set of pages, and then free the ones at address 0. However at that point grub_efi_store_alloc() wasn't yet called, so freeing the pages at 0 using grub_efi_free_pages() which calls grub_efi_drop_alloc() isn't necessary, so let's call b->free_pages() instead. The call to grub_efi_drop_alloc() doesn't seem particularly harmful, because it seems to do nothing if the allocation it is asked to drop isn't on the list, but the call to it is obviously unnecessary here. Signed-off-by: Mate Kukri Reviewed-by: Daniel Kiper --- grub-core/kern/efi/mm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c index 739229e7b..bc18b149d 100644 --- a/grub-core/kern/efi/mm.c +++ b/grub-core/kern/efi/mm.c @@ -150,7 +150,7 @@ grub_efi_allocate_pages_real (grub_efi_physical_address_t address, so reallocate another one. */ address = GRUB_EFI_MAX_USABLE_ADDRESS; status = b->allocate_pages (alloctype, memtype, pages, &address); - grub_efi_free_pages (0, pages); + b->free_pages (0, pages); if (status != GRUB_EFI_SUCCESS) { grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); From 55d35d62831af122fadf33c4bc8cd53aada949cb Mon Sep 17 00:00:00 2001 From: Mate Kukri Date: Wed, 12 Jun 2024 16:14:21 +0100 Subject: [PATCH 160/402] kern/efi/mm: Detect calls to grub_efi_drop_alloc() with wrong page counts Silently keeping entries in the list if the address matches, but the page count doesn't is a bad idea, and can lead to double frees. grub_efi_free_pages() have already freed parts of this block by this point, and thus keeping the whole block in the list and freeing it again at exit can lead to double frees. Signed-off-by: Mate Kukri Reviewed-by: Daniel Kiper --- grub-core/kern/efi/mm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c index bc18b149d..df7bf2869 100644 --- a/grub-core/kern/efi/mm.c +++ b/grub-core/kern/efi/mm.c @@ -95,8 +95,10 @@ grub_efi_drop_alloc (grub_efi_physical_address_t address, for (eap = NULL, ea = efi_allocated_memory; ea; eap = ea, ea = ea->next) { - if (ea->address != address || ea->pages != pages) - continue; + if (ea->address != address) + continue; + if (ea->pages != pages) + grub_fatal ("grub_efi_drop_alloc() called with wrong page count"); /* Remove the current entry from the list. */ if (eap) From f96df6fe9f6faa328c82820af88f14af07b2c9b9 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Mon, 17 Jun 2024 14:44:08 +0300 Subject: [PATCH 161/402] fs/zfs/zfs: Add support for zstd compression Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/Makefile.core.def | 1 + grub-core/fs/zfs/zfs.c | 38 +++++++++++++++++++++++++++++++++++++ include/grub/zfs/zio.h | 1 + 3 files changed, 40 insertions(+) diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 7fa9446bd..705d73fab 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1601,6 +1601,7 @@ module = { common = fs/zfs/zfs_lz4.c; common = fs/zfs/zfs_sha256.c; common = fs/zfs/zfs_fletcher.c; + cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/zstd'; }; module = { diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index dbe4ea503..3fdf9bda8 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -57,6 +57,8 @@ #include #include +#include + GRUB_MOD_LICENSE ("GPLv3+"); #define ZPOOL_PROP_BOOTFS "bootfs" @@ -293,6 +295,7 @@ static const char *spa_feature_names[] = { "org.open-zfs:large_blocks", "com.klarasystems:vdev_zaps_v2", "com.delphix:head_errlog", + "org.freebsd:zstd_compress", NULL }; @@ -314,6 +317,40 @@ zlib_decompress (void *s, void *d, return grub_errno; } +static grub_err_t +zstd_decompress (void *ibuf, void *obuf, grub_size_t isize, + grub_size_t osize) +{ + grub_size_t zstd_ret; + grub_uint32_t c_len; + grub_uint8_t *byte_buf = (grub_uint8_t *) ibuf; + + if (isize < 8) + return grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "zstd data too short"); + + c_len = grub_be_to_cpu32 (grub_get_unaligned32 (byte_buf)); + + if (c_len > isize - 8) + return grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, + "zstd data announced size overflow"); + + /* + * ZFS uses non-stadard magic for zstd streams. Rather than adjusting + * library functions, replace non-standard magic with standard one. + */ + byte_buf[4] = 0x28; + byte_buf[5] = 0xb5; + byte_buf[6] = 0x2f; + byte_buf[7] = 0xfd; + zstd_ret = ZSTD_decompress (obuf, osize, byte_buf + 4, c_len + 4); + + if (ZSTD_isError (zstd_ret)) + return grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, + "zstd data corrupted (error %d)", (int) zstd_ret); + + return GRUB_ERR_NONE; +} + static grub_err_t zle_decompress (void *s, void *d, grub_size_t slen, grub_size_t dlen) @@ -364,6 +401,7 @@ static decomp_entry_t decomp_table[ZIO_COMPRESS_FUNCTIONS] = { {"gzip-9", zlib_decompress}, /* ZIO_COMPRESS_GZIP9 */ {"zle", zle_decompress}, /* ZIO_COMPRESS_ZLE */ {"lz4", lz4_decompress}, /* ZIO_COMPRESS_LZ4 */ + {"zstd", zstd_decompress}, /* ZIO_COMPRESS_ZSTD */ }; static grub_err_t zio_read_data (blkptr_t * bp, grub_zfs_endian_t endian, diff --git a/include/grub/zfs/zio.h b/include/grub/zfs/zio.h index 19ce136bb..997b0c4d4 100644 --- a/include/grub/zfs/zio.h +++ b/include/grub/zfs/zio.h @@ -89,6 +89,7 @@ enum zio_compress { ZIO_COMPRESS_GZIP9, ZIO_COMPRESS_ZLE, ZIO_COMPRESS_LZ4, + ZIO_COMPRESS_ZSTD, ZIO_COMPRESS_FUNCTIONS }; From 9a2134a70fc6c7cad6d6954db5ab6732828dbfc1 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Mon, 17 Jun 2024 14:44:09 +0300 Subject: [PATCH 162/402] tests: Add test for ZFS zstd Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- tests/util/grub-fs-tester.in | 2 +- tests/zfs_test.in | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/util/grub-fs-tester.in b/tests/util/grub-fs-tester.in index 2f5e9bef6..cac58dafa 100644 --- a/tests/util/grub-fs-tester.in +++ b/tests/util/grub-fs-tester.in @@ -785,7 +785,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 1); do sleep 1 "zfs" create -o casesensitivity=insensitive "$FSLABEL"/"grub fs" sleep 1;; - x"zfs_lzjb" | xzfs_gzip | xzfs_zle) + x"zfs_lzjb" | xzfs_gzip | xzfs_zle | xzfs_zstd) "zpool" create -O compression=${fs/zfs_/} -R "$MNTPOINTRW" "$FSLABEL" "${MOUNTDEVICE}" sleep 1 "zfs" create -o compression=${fs/zfs_/} "$FSLABEL"/"grub fs" diff --git a/tests/zfs_test.in b/tests/zfs_test.in index 58cc25b22..0d0a57f7d 100644 --- a/tests/zfs_test.in +++ b/tests/zfs_test.in @@ -19,6 +19,7 @@ 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 From 828717833fd18958199d731e366ef3fe997f2444 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Mon, 17 Jun 2024 15:56:30 +0300 Subject: [PATCH 163/402] disk/cryptodisk: Fix translatable message Fixes: https://savannah.gnu.org/bugs/?64408 Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/disk/cryptodisk.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index f98acade6..5e1eb2743 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -1359,9 +1359,9 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) if (keyfile_offset > keyfile->size) return grub_error (GRUB_ERR_OUT_OF_RANGE, - N_("Keyfile offset, %llu, is greater than" - "keyfile size, %" PRIuGRUB_UINT64_T), - keyfile_offset, keyfile->size); + N_("Keyfile offset, %llu, is greater than " + "keyfile size, %llu"), + keyfile_offset, (unsigned long long) keyfile->size); if (grub_file_seek (keyfile, (grub_off_t) keyfile_offset) == (grub_off_t) -1) return grub_errno; From f171122f03a5e0feca9541b50ef1bfd693c5fb21 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Mon, 17 Jun 2024 15:56:31 +0300 Subject: [PATCH 164/402] loader/emu/linux: Fix determination of program name Current code works only if package matches binary name transformation rules. It's often true but is not guaranteed. Fixes: https://savannah.gnu.org/bugs/?64410 Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/loader/emu/linux.c | 2 +- include/grub/emu/misc.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/loader/emu/linux.c b/grub-core/loader/emu/linux.c index 7de3f7f86..638c55bf8 100644 --- a/grub-core/loader/emu/linux.c +++ b/grub-core/loader/emu/linux.c @@ -68,7 +68,7 @@ grub_linux_boot (void) } if (kexecute < 1) - grub_fatal (N_("use '"PACKAGE"-emu --kexec' to force a system restart")); + grub_fatal (N_("use '%s %s' to force a system restart"), program_name, "--kexec"); grub_dprintf ("linux", "Performing 'systemctl kexec' (%s) ", (kexecute==1) ? "do-or-die" : "just-in-case"); diff --git a/include/grub/emu/misc.h b/include/grub/emu/misc.h index 01056954b..fa959ebe7 100644 --- a/include/grub/emu/misc.h +++ b/include/grub/emu/misc.h @@ -31,7 +31,7 @@ #include extern int verbosity; -extern const char *program_name; +extern const char *EXPORT_VAR(program_name); void grub_init_all (void); void grub_fini_all (void); From 0cfec355d027a5a0692d52d6d6dbb8da0ab48f61 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Mon, 17 Jun 2024 15:59:56 +0300 Subject: [PATCH 165/402] osdep/devmapper/getroot: Unmark 2 strings for translation First they're use macros so they can't be translated as-is. Second there is no point in translating them as they're too technical. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/osdep/devmapper/getroot.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/osdep/devmapper/getroot.c b/grub-core/osdep/devmapper/getroot.c index 3b37336bc..5fb21aff9 100644 --- a/grub-core/osdep/devmapper/getroot.c +++ b/grub-core/osdep/devmapper/getroot.c @@ -262,7 +262,7 @@ grub_util_pull_devmapper (const char *os_dev) params); cipher = grub_strndup (c, seek_head - c); if (cipher == NULL) - grub_util_error (_("could not strndup cipher of length `%" PRIuGRUB_SIZE "'"), (grub_size_t) (seek_head - c)); + grub_util_error ("could not strndup cipher of length `%" PRIuGRUB_SIZE "'", (grub_size_t) (seek_head - c)); remaining -= seek_head - c + 1; c = seek_head + 1; @@ -273,7 +273,7 @@ grub_util_pull_devmapper (const char *os_dev) params); cipher_mode = grub_strndup (c, seek_head - c); if (cipher_mode == NULL) - grub_util_error (_("could not strndup cipher_mode of length `%" PRIuGRUB_SIZE "'"), (grub_size_t) (seek_head - c)); + grub_util_error ("could not strndup cipher_mode of length `%" PRIuGRUB_SIZE "'", (grub_size_t) (seek_head - c)); remaining -= seek_head - c + 1; c = seek_head + 1; From ab9fe8030022a01f3c6c8e0b070f4a381e71a48f Mon Sep 17 00:00:00 2001 From: Tobias Heider Date: Mon, 17 Jun 2024 17:10:26 +0200 Subject: [PATCH 166/402] loader/efi/fdt: Add fdtdump command to access device tree The fdtdump command allows dumping arbitrary device tree properties and saving them to a variable similar to the smbios command. This is useful in scripts where further actions such as selecting a kernel or loading another device tree depend on the compatible or model values of the device tree provided by the firmware. For now only the root level properties of the dtb are exposed. Signed-off-by: Tobias Heider Reviewed-by: Daniel Kiper --- docs/grub.texi | 26 +++++++++++++++++++++ grub-core/loader/efi/fdt.c | 46 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/docs/grub.texi b/docs/grub.texi index f3bdc2564..a050dc0fc 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -4373,6 +4373,7 @@ you forget a command, you can run the command @command{help} * eval:: Evaluate agruments as GRUB commands * export:: Export an environment variable * false:: Do nothing, unsuccessfully +* fdtdump:: Retrieve device tree information * fwsetup:: Reboot into the firmware setup menu * gdbinfo:: Provide info for debugging with GDB * gettext:: Translate a string @@ -4904,6 +4905,31 @@ such as @code{if} and @code{while} (@pxref{Shell-like scripting}). @end deffn +@node fdtdump +@subsection fdtdump + +@deffn Command fdtdump @ + [@option{--prop} @var{prop}] @ + [@option{--set} @var{variable}] +Retrieve device tree information. + +The @command{fdtdump} command returns the value of a property in the device +tree provided by the firmware. The @option{--prop} option determines which +property to select. + +The default action is to print the value of the requested field to the console, +but a variable name can be specified with @option{--set} to store the value +instead of printing it. + +For example, this will store and then display the model string. + +@example +fdtdump --prop model --set machine_model +echo $machine_model +@end example +@end deffn + + @node fwsetup @subsection fwsetup diff --git a/grub-core/loader/efi/fdt.c b/grub-core/loader/efi/fdt.c index 439964b9c..e510b3491 100644 --- a/grub-core/loader/efi/fdt.c +++ b/grub-core/loader/efi/fdt.c @@ -1,6 +1,7 @@ /* * GRUB -- GRand Unified Bootloader * Copyright (C) 2013-2015 Free Software Foundation, Inc. + * Copyright (C) 2024 Canonical, Ltd. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,9 +19,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -36,6 +39,13 @@ static void *fdt; sizeof (FDT_ADDR_CELLS_STRING) + \ sizeof (FDT_SIZE_CELLS_STRING)) +static const struct grub_arg_option options_fdtdump[] = { + {"prop", 'p', 0, N_("Get property."), N_("prop"), ARG_TYPE_STRING}, + {"set", '\0', 0, N_("Store the value in the given variable name."), + N_("variable"), ARG_TYPE_STRING}, + {0, 0, 0, 0, 0, 0} +}; + void * grub_fdt_load (grub_size_t additional_size) { @@ -167,10 +177,45 @@ out: return grub_errno; } +static grub_err_t +grub_cmd_fdtdump (grub_extcmd_context_t ctxt, + int argc __attribute__ ((unused)), + char **argv __attribute__ ((unused))) +{ + struct grub_arg_list *state = ctxt->state; + const char *value = NULL; + void *fw_fdt; + + fw_fdt = grub_efi_get_firmware_fdt (); + if (fw_fdt == NULL) + return grub_error (GRUB_ERR_IO, + N_("No device tree found")); + + if (state[0].set) + value = grub_fdt_get_prop (fw_fdt, 0, state[0].arg, NULL); + + if (value == NULL) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("failed to retrieve the prop field")); + + if (state[1].set) + grub_env_set (state[1].arg, value); + else + grub_printf ("%s\n", value); + + return GRUB_ERR_NONE; +} + static grub_command_t cmd_devicetree; +static grub_extcmd_t cmd_fdtdump; GRUB_MOD_INIT (fdt) { + cmd_fdtdump = + grub_register_extcmd ("fdtdump", grub_cmd_fdtdump, 0, + N_("[-p] [--set variable]"), + N_("Retrieve device tree information."), + options_fdtdump); cmd_devicetree = grub_register_command_lockdown ("devicetree", grub_cmd_devicetree, 0, N_("Load DTB file.")); @@ -179,4 +224,5 @@ GRUB_MOD_INIT (fdt) GRUB_MOD_FINI (fdt) { grub_unregister_command (cmd_devicetree); + grub_unregister_extcmd (cmd_fdtdump); } From b53ec06a1d6f22ffc1139cbfc0f292e4ca2da9cd Mon Sep 17 00:00:00 2001 From: Thomas Schmitt Date: Mon, 17 Jun 2024 21:03:00 +0200 Subject: [PATCH 167/402] util/grub-mkrescue: Check existence of option arguments As reported by Victoriia Egorova in bug 65880, grub-mkrescue does not verify that the expected argument of an option like -d or -k does really exist in argv. So, check the loop counter before incrementing it inside the loop which copies argv to argp_argv. Issue an error message similar to what older versions of grub-mkrescue did with a missing argument, e.g. 2.02. Fixes: https://savannah.gnu.org/bugs/index.php?65880 Signed-off-by: Thomas Schmitt Reviewed-by: Daniel Kiper --- util/grub-mkrescue.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util/grub-mkrescue.c b/util/grub-mkrescue.c index abcc1c2f5..6dc71a8a1 100644 --- a/util/grub-mkrescue.c +++ b/util/grub-mkrescue.c @@ -477,6 +477,8 @@ main (int argc, char *argv[]) for (i = 1; i < argc; i++) { if (strcmp (argv[i], "-output") == 0) { + if (i + 1 >= argc) + grub_util_error ("%s -- '%s'", _("option requires an argument"), argv[i]); argp_argv[argp_argc++] = (char *) "--output"; i++; argp_argv[argp_argc++] = argv[i]; @@ -485,6 +487,8 @@ main (int argc, char *argv[]) switch (args_to_eat (argv[i])) { case 2: + if (i + 1 >= argc) + grub_util_error ("%s -- '%s'", _("option requires an argument"), argv[i]); argp_argv[argp_argc++] = argv[i++]; /* Fallthrough */ case 1: From 9537f4403dd836d5a8a1c4e57b165837fc7239cf Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Wed, 17 Jul 2024 14:46:46 +0800 Subject: [PATCH 168/402] commands/bli: Fix crash in get_part_uuid() The get_part_uuid() function made an assumption that the target GRUB device is a partition device and accessed device->disk->partition without checking for NULL. There are four situations where this assumption is problematic: 1. The device is a net device instead of a disk. 2. The device is an abstraction device, like LVM, RAID, or CRYPTO, which is mostly logical "disk" ((lvmid/) and so on). 3. Firmware RAID may present the ESP to GRUB as an EFI disk (hd0) device if it is contained within a Linux software RAID. 4. When booting from a CD-ROM, the ESP is a VFAT image indexed by the El Torito boot catalog. The boot device is set to (cd0), corresponding to the CD-ROM image mounted as an ISO 9660 filesystem. As a result, get_part_uuid() could lead to a NULL pointer dereference and trigger a synchronous exception during boot if the ESP falls into one of these categories. This patch fixes the problem by adding the necessary checks to handle cases where the ESP is not a partition device. Additionally, to avoid disrupting the boot process, this patch relaxes the severity of the errors in this context to non-critical. Errors will be logged, but they will not prevent the boot process from continuing. Fixes: e0fa7dc84 (bli: Add a module for the Boot Loader Interface) Signed-off-by: Michael Chang Reviewed-By: Oliver Steffen Reviewed-by: Daniel Kiper --- grub-core/commands/bli.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/grub-core/commands/bli.c b/grub-core/commands/bli.c index e0d8a54f7..298c5f70a 100644 --- a/grub-core/commands/bli.c +++ b/grub-core/commands/bli.c @@ -48,6 +48,22 @@ get_part_uuid (const char *device_name, char **part_uuid) if (device == NULL) return grub_error (grub_errno, N_("cannot open device: %s"), device_name); + if (device->disk == NULL) + { + grub_dprintf ("bli", "%s is not a disk device, partuuid skipped\n", device_name); + *part_uuid = NULL; + grub_device_close (device); + return GRUB_ERR_NONE; + } + + if (device->disk->partition == NULL) + { + grub_dprintf ("bli", "%s has no partition, partuuid skipped\n", device_name); + *part_uuid = NULL; + grub_device_close (device); + return GRUB_ERR_NONE; + } + disk = grub_disk_open (device->disk->name); if (disk == NULL) { @@ -99,7 +115,7 @@ set_loader_device_part_uuid (void) status = get_part_uuid (device_name, &part_uuid); - if (status == GRUB_ERR_NONE) + if (status == GRUB_ERR_NONE && part_uuid) status = grub_efi_set_variable_to_string ("LoaderDevicePartUUID", &bli_vendor_guid, part_uuid, GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS | GRUB_EFI_VARIABLE_RUNTIME_ACCESS); @@ -117,4 +133,6 @@ GRUB_MOD_INIT (bli) GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS | GRUB_EFI_VARIABLE_RUNTIME_ACCESS); set_loader_device_part_uuid (); + /* No error here is critical, other than being logged */ + grub_print_error (); } From ab1e6fc044393fb91cda6b60cacdb2dabf4f6bea Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Sun, 4 Aug 2024 11:32:51 -0500 Subject: [PATCH 169/402] docs: Document all GRUB modules Add documentation for all GRUB modules contained in the source code tree. When possible, cross-references to additional detail on commands was added from their corresponding module documentation. In addition, documentation for the file command was added. Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- docs/grub.texi | 2118 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 2114 insertions(+), 4 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index a050dc0fc..2ea6c56d1 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -3878,15 +3878,405 @@ using BIOS or EFI functions (no ATA, USB or IEEE1275). @node Modules @chapter Modules -In this chapter, we list all modules that are available in GRUB (currently incomplete). +In this chapter, we list all modules that are available in GRUB. Modules can be loaded via the @command{insmod} (@pxref{insmod}) command. @menu -* bli:: +* acpi_module:: +* adler32_module:: +* affs_module:: +* afs_module:: +* afsplitter_module:: +* ahci_module:: +* all_video_module:: +* aout_module:: +* appleldr_module:: +* archelp_module:: +* at_keyboard_module:: +* ata_module:: +* backtrace_module:: +* bfs_module:: +* biosdisk_module:: +* bitmap_module:: +* bitmap_scale_module:: +* bli_module:: +* blocklist_module:: +* boot_module:: +* boottime_module:: +* bsd_module:: +* bswap_test_module:: +* btrfs_module:: +* bufio_module:: +* cacheinfo_module:: +* cat_module:: +* cbfs_module:: +* cbls_module:: +* cbmemc_module:: +* cbtable_module:: +* cbtime_module:: +* chain_module:: +* cmdline_cat_test_module:: +* cmosdump_module:: +* cmostest_module:: +* cmp_module:: +* cmp_test_module:: +* configfile_module:: +* cpio_module:: +* cpio_be_module:: +* cpuid_module:: +* crc64_module:: +* crypto_module:: +* cryptodisk_module:: +* cs5536_module:: +* ctz_test_module:: +* date_module:: +* datehook_module:: +* datetime_module:: +* disk_module:: +* diskfilter_module:: +* div_module:: +* div_test_module:: +* dm_nv_module:: +* drivemap_module:: +* echo_module:: +* efi_gop_module:: +* efi_uga_module:: +* efiemu_module:: +* efifwsetup_module:: +* efinet_module:: +* efitextmode_module:: +* ehci_module:: +* elf_module:: +* emunet_module:: +* emupci_module:: +* erofs_module:: +* escc_module:: +* eval_module:: +* exfat_module:: +* exfctest_module:: +* ext2_module:: +* extcmd_module:: +* f2fs_module:: +* fat_module:: +* fdt_module:: +* file_module:: +* fixvideo_module:: +* font_module:: +* freedos_module:: +* fshelp_module:: +* functional_test_module:: +* gcry_arcfour_module:: +* gcry_blowfish_module:: +* gcry_camellia_module:: +* gcry_cast5_module:: +* gcry_crc_module:: +* gcry_des_module:: +* gcry_dsa_module:: +* gcry_idea_module:: +* gcry_md4_module:: +* gcry_md5_module:: +* gcry_rfc2268_module:: +* gcry_rijndael_module:: +* gcry_rmd160_module:: +* gcry_rsa_module:: +* gcry_seed_module:: +* gcry_serpent_module:: +* gcry_sha1_module:: +* gcry_sha256_module:: +* gcry_sha512_module:: +* gcry_tiger_module:: +* gcry_twofish_module:: +* gcry_whirlpool_module:: +* gdb_module:: +* geli_module:: +* gettext_module:: +* gfxmenu_module:: +* gfxterm_module:: +* gfxterm_background_module:: +* gfxterm_menu_module:: +* gptsync_module:: +* gzio_module:: +* halt_module:: +* hashsum_module:: +* hdparm_module:: +* hello_module:: +* help_module:: +* hexdump_module:: +* hfs_module:: +* hfsplus_module:: +* hfspluscomp_module:: +* http_module:: +* ieee1275_fb_module:: +* iorw_module:: +* iso9660_module:: +* jfs_module:: +* jpeg_module:: +* json_module:: +* keylayouts_module:: +* keystatus_module:: +* ldm_module:: +* legacy_password_test_module:: +* legacycfg_module:: +* linux_module:: +* linux16_module:: +* loadbios_module:: +* loadenv_module:: +* loopback_module:: +* ls_module:: +* lsacpi_module:: +* lsapm_module:: +* lsdev_module:: +* lsefi_module:: +* lsefimmap_module:: +* lsefisystab_module:: +* lsmmap_module:: +* lspci_module:: +* lssal_module:: +* lsspd_module:: +* lsxen_module:: +* luks_module:: +* luks2_module:: +* lvm_module:: +* lzopio_module:: +* macbless_module:: +* macho_module:: +* mda_text_module:: +* mdraid09_module:: +* mdraid09_be_module:: +* mdraid1x_module:: +* memdisk_module:: +* memrw_module:: +* memtools_module:: +* minicmd_module:: +* minix_module:: +* minix2_module:: +* minix2_be_module:: +* minix3_module:: +* minix3_be_module:: +* minix_be_module:: +* mmap_module:: +* morse_module:: +* mpi_module:: +* msdospart_module:: +* mul_test_module:: +* multiboot_module:: +* multiboot2_module:: +* nand_module:: +* nativedisk_module:: +* net_module:: +* newc_module:: +* nilfs2_module:: +* normal_module:: +* ntfs_module:: +* ntfscomp_module:: +* ntldr_module:: +* odc_module:: +* offsetio_module:: +* ofnet_module:: +* ohci_module:: +* part_acorn_module:: +* part_amiga_module:: +* part_apple_module:: +* part_bsd_module:: +* part_dfly_module:: +* part_dvh_module:: +* part_gpt_module:: +* part_msdos_module:: +* part_plan_module:: +* part_sun_module:: +* part_sunpc_module:: +* parttool_module:: +* password_module:: +* password_pbkdf2_module:: +* pata_module:: +* pbkdf2_module:: +* pbkdf2_test_module:: +* pci_module:: +* pcidump_module:: +* pgp_module:: +* plainmount_module:: +* plan9_module:: +* play_module:: +* png_module:: +* priority_queue_module:: +* probe_module:: +* procfs_module:: +* progress_module:: +* pxe_module:: +* pxechain_module:: +* raid5rec_module:: +* raid6rec_module:: +* random_module:: +* rdmsr_module:: +* read_module:: +* reboot_module:: +* regexp_module:: +* reiserfs_module:: +* relocator_module:: +* romfs_module:: +* scsi_module:: +* sdl_module:: +* search_module:: +* search_fs_file_module:: +* search_fs_uuid_module:: +* search_label_module:: +* sendkey_module:: +* serial_module:: +* setjmp_module:: +* setjmp_test_module:: +* setpci_module:: +* sfs_module:: +* shift_test_module:: +* signature_test_module:: +* sleep_module:: +* sleep_test_module:: +* smbios_module:: +* spkmodem_module:: +* squash4_module:: +* strtoull_test_module:: +* suspend_module:: +* syslinuxcfg_module:: +* tar_module:: +* terminal_module:: +* terminfo_module:: +* test_module:: +* test_blockarg_module:: +* testload_module:: +* testspeed_module:: +* tftp_module:: +* tga_module:: +* time_module:: +* tpm_module:: +* tr_module:: +* trig_module:: +* true_module:: +* truecrypt_module:: +* ubootnet_module:: +* udf_module:: +* ufs1_module:: +* ufs1_be_module:: +* ufs2_module:: +* uhci_module:: +* usb_module:: +* usb_keyboard_module:: +* usbms_module:: +* usbserial_common_module:: +* usbserial_ftdi_module:: +* usbserial_pl2303_module:: +* usbserial_usbdebug_module:: +* usbtest_module:: +* vbe_module:: +* verifiers_module:: +* vga_module:: +* vga_text_module:: +* video_module:: +* video_bochs_module:: +* video_cirrus_module:: +* video_colors_module:: +* video_fb_module:: +* videoinfo_module:: +* videotest_module:: +* videotest_checksum_module:: +* wrmsr_module:: +* xen_boot_module:: +* xfs_module:: +* xnu_module:: +* xnu_uuid_module:: +* xnu_uuid_test_module:: +* xzio_module:: +* zfs_module:: +* zfscrypt_module:: +* zfsinfo_module:: +* zstd_module:: + @end menu -@node bli +@node acpi_module +@section acpi +This module provides the command @command{acpi} for loading / replacing Advanced +Configuration and Power Interface (ACPI) tables. Please @pxref{acpi} for more +information. + +@node adler32_module +@section adler32 +This module provides the library implementation for the adler32 checksum. +This is used as part of LZO decompression / compression. + +@node affs_module +@section affs +This module provides support for the Amiga Fast FileSystem (AFFS). + +@node afs_module +@section afs +This module provides support for the AtheOS File System (AFS). + +@node afsplitter_module +@section afsplitter +This module provides library support for the Anti forensic information splitter +(AFS) operation @code{AF_merge}. This is used by LUKS and LUKS2. + +@node ahci_module +@section ahci +This module provides support for the Advanced Host Controller Interface protocol +to access disks supporting this standard. AHCI is often an option for Serial +ATA (SATA) controllers (meant to replace the older IDE protocol). + +@node all_video_module +@section all_video +This is a "dummy module" with no actual function except to load all other video +modules as dependencies (a convenient way to load all video modules). + +@node aout_module +@section aout +This module provides support for loading files packaged in the "a.out" format. +The "a.out" format is considered to be an older format than some alternatives +such as "ELF", for example support for the "a.out" format was removed from the +Linux kernel in 5.18. + +@node appleldr_module +@section appleldr +This module provides support for loading files on a BIOS / EFI based Apple Mac +computer (Intel based Macs). + +@node archelp_module +@section archelp +This module provides Archive Helper functions for archive based file systems +such as TAR and CPIO archives. + +@node at_keyboard_module +@section at_keyboard +This module provides support for the AT keyboard input for the GRUB terminal. + +@node ata_module +@section ata +This modules provides support for direct ATA and ATAPI access to compatible +disks. + +@node backtrace_module +@section backtrace +This module provides the command @command{backtrace} for printing a backtrace +to the terminal for the current call stack. + +@node bfs_module +@section bfs +This module provides support for the BeOS "Be File System" (BFS). + +@node biosdisk_module +@section biosdisk +This module provides support for booting from a bootable removable disk such +as a CD-ROM, BD-ROM, etc. + +@node bitmap_module +@section bitmap +This module provides support for reading and interacting with bitmap image +files. + +@node bitmap_scale_module +@section bitmap_scale +This module provides support for scaling bitmap image files. + +@node bli_module @section bli This module provides basic support for the Boot Loader Interface. The Boot Loader Interface specifies a set of EFI variables that are used to communicate @@ -3907,6 +4297,1638 @@ number is stored in @code{LoaderInfo}. This module is only available on UEFI platforms. +@node blocklist_module +@section blocklist +This module provides support for the command @command{blocklist} to list +blocks for a given file. Please @pxref{blocklist} for more information. + +@node boot_module +@section boot +This module provides support for the command @command{boot} to boot an +operating system. Please @pxref{boot} for more information. + +@node boottime_module +@section boottime +This module provides support for the command @command{boottime} to display +time taken to perform various GRUB operations. This module is only available +when GRUB is built with the conditional compile option @code{BOOT_TIME_STATS}. + +@node bsd_module +@section bsd +This module provides support for loading BSD operating system images via +commands such as: @command{kfreebsd_loadenv}, @command{kfreebsd_module_elf}, +@command{kfreebsd_module}, @command{kfreebsd}, @command{knetbsd_module_elf}, +@command{knetbsd_module}, @command{knetbsd}, @command{kopenbsd}, and +@command{kopenbsd_ramdisk}. Please @pxref{Loader commands} for more info. + +@node bswap_test_module +@section bswap_test +This module is intended for performing a functional test of the byte swapping +functionality of GRUB. + +@node btrfs_module +@section btrfs +This module provides support for the B-Tree File System (BTRFS). + +@node bufio_module +@section bufio +This module is a library module for support buffered I/O of files to support +file reads performed in other modules. + +@node cacheinfo_module +@section cacheinfo +This module provides support for the command @command{cacheinfo} which provides +statistics on disk cache accesses. This module is only built if +@code{DISK_CACHE_STATS} is enabled. + +@node cat_module +@section cat +This module provides support for the command @command{cat} which outputs the +content of a file to the terminal. Please @pxref{cat} for more info. + +@node cbfs_module +@section cbfs +This module provides support for the Coreboot File System (CBFS) which is an +archive based file system. + +@node cbls_module +@section cbls +This module provides support for the command @command{lscoreboot} to list the +Coreboot tables. + +@node cbmemc_module +@section cbmemc +This module provides support for the command @command{cbmemc} to show the +content of the Coreboot Memory console. + +@node cbtable_module +@section cbtable +This module provides support for accessing the Coreboot tables. + +@node cbtime_module +@section cbtime +This module provides support for the command @command{coreboot_boottime} to show +the Coreboot boot time statistics. + +@node chain_module +@section chain +This module provides support for the command @command{chainloader} to boot +another bootloader. Please @pxref{chainloader} for more information. + +@node cmdline_cat_test_module +@section cmdline_cat_test +This module is intended for performing a functional test of the @command{cat} +command of GRUB. + +@node cmosdump_module +@section cmosdump +This module provides support for the command @command{cmosdump} to show a raw +dump of the CMOS contents. Please @pxref{cmosdump} for more information. + +@node cmostest_module +@section cmostest +This module provides support for the commands @command{cmostest}, +@command{cmosclean}, and @command{cmosset} to interact with a CMOS. +@xref{cmostest} / @pxref{cmosclean} for more information. + +@node cmp_module +@section cmp +This module provides support for the command @command{cmp} to compare the +content of two files. @xref{cmp} for more information. + +@node cmp_test_module +@section cmp_test +This module is intended for performing a functional test of relational +operations in GRUB. Note that this module is *not* associated with the +@command{cmp} command and does not test the @command{cmp} command. + +@node configfile_module +@section configfile +This module provides support for the commands: @command{configfile}, +@command{source}, @command{extract_entries_source}, +@command{extract_entries_configfile}, @command{.} (dot command). +@xref{configfile} / @pxref{source}. + +@node cpio_module +@section cpio +This module provides support for the CPIO archive file format. This module is +for the "bin" version of CPIO (default of GNU CPIO) supporting around 2GB. + +@node cpio_be_module +@section cpio_be +This module provides support for the CPIO archive file format in big-endian +format. This module is for the "bin" version of CPIO (default of GNU CPIO) +supporting around 2GB. + +@node cpuid_module +@section cpuid +This module provides support for the command @command{cpuid} to test for +various CPU features. @xref{cpuid} for more information. + +@node crc64_module +@section crc64 +This module provides support for the CRC64 operation. + +@node crypto_module +@section crypto +This module provides library support for various base cryptography operations +in GRUB. + +@node cryptodisk_module +@section cryptodisk +This module provides support for the command @command{cryptomount} to interact +with encrypted file systems. @xref{cryptomount} for more information. + +@node cs5536_module +@section cs5536 +This module provides support for the AMD Geode CS5536 companion device. + +@node ctz_test_module +@section ctz_test +This module is intended for performing a functional test of the ctz functions +in GRUB used to Count Trailing Zeros. + +@node date_module +@section date +This module provides support for the command @command{date} to get the date/time +or set the date/time. @xref{date} for more information. + +@node datehook_module +@section datehook +This module provides support for populating / providing the environment +variables @code{YEAR}, @code{MONTH}, @code{DAY}, @code{HOUR}, @code{MINUTE}, +@code{SECOND}, @code{WEEKDAY}. + +@node datetime_module +@section datetime +This module provides library support for getting and setting the date / time +from / to a hardware clock device. + +@node disk_module +@section disk +This module provides library support for writing to a storage disk. + +@node diskfilter_module +@section diskfilter +This module provides library support for reading a disk RAID array. + +@node div_module +@section div +This module provides library support for some operations such as divmod. + +@node div_test_module +@section div_test +This module is intended for performing a functional test of the divmod function +in GRUB. + +@node dm_nv_module +@section dm_nv +This module provides support for handling some Nvidia "fakeraid" disk devices. + +@node drivemap_module +@section drivemap +This module provides support for the @command{drivemap} to manage BIOS drive +mappings. @xref{drivemap} for more information. + +@node echo_module +@section echo +This module provides support for the @command{echo} to display a line of text. +@xref{echo} for more information. + +@node efi_gop_module +@section efi_gop +This module provides support for the UEFI video output protocol "Graphics +Output Protocol" (GOP). + +@node efi_uga_module +@section efi_uga +This module provides support for the EFI video protocol "Universal Graphic +Adapter" (UGA). + +@node efiemu_module +@section efiemu +This module provides support for the commands @command{efiemu_loadcore}, +@command{efiemu_prepare}, and @command{efiemu_unload}. This provides an EFI +emulation. + +@node efifwsetup_module +@section efifwsetup +This modules provides support for the command @command{fwsetup} to reboot into +the firmware setup menu. @xref{fwsetup} for more information. + +@node efinet_module +@section efinet +This module provides support for UEFI Network Booting for loading images and +data from the network. + +@node efitextmode_module +@section efitextmode +This module provides support for command @command{efitextmode} to get and set +output mode resolution. @xref{efitextmode} for more information. + +@node ehci_module +@section ehci +This module provides support for the USB Enhanced Host Controller Interface +(EHCI) specification (USB 2.0). + +@node elf_module +@section elf +This module provides support for loading Executable and Linkable Format (ELF) +files. + +@node emunet_module +@section emunet +This module provides support for an emulated network card in GRUB. + +@node emupci_module +@section emupci +This module provides support for an emulated PCI bus in GRUB. + +@node erofs_module +@section erofs +This module provides support for the Enhanced Read Only File System (EROFS). + +@node escc_module +@section escc +This module provides support for the "mac-io" terminal device on PowerPC. + +@node eval_module +@section eval +This module provides support for command @command{eval} to evaluate the provided +input as a sequence of GRUB commands. @xref{eval} for more information. + +@node exfat_module +@section exfat +This module provides support for the Extensible File Allocation Table (exFAT) +file system in GRUB. + +@node exfctest_module +@section exfctest +This module is intended to provide an Example Functional Test of GRUB functions +to use as a template for developing other GRUB functional tests. + +@node ext2_module +@section ext2 +This module provides support for the Extended File System versions 2, 3, and 4 +(ext2, ext3, and ext4) file systems in GRUB. + +@node extcmd_module +@section extcmd +This module is a support module to provide wrapper functions for registering +other module commands depending on the state of the lockdown variable. + +@node f2fs_module +@section f2fs +This module provides support for the Flash-Friendly File System (F2FS) in GRUB. + +@node fat_module +@section fat +This module provides support for the File Allocation Table 12-bit, 16-bit, and +32-bit (FAT12, FAT16, and FAT32) file systems in GRUB. + +@node fdt_module +@section fdt +This module provides support for the commands @command{fdtdump} and +@command{devicetree} to dump the contents of a device tree blob (.dtb) to the +console and to load a device tree blob (.dtb) from a filesystem, for +later use by a Linux kernel, respectively. @xref{devicetree} and +@pxref{fdtdump} for more information. + +@node file_module +@section file +This module provides support for the command @command{file} to test if the +provided filename is of the specified type. @xref{file} for more information. + +@node fixvideo_module +@section fixvideo +This module provides support for the command @command{fix_video} to fix video +problems in specific PCIe video devices by "patching" specific device register +settings. Currently supports Intel 945GM (PCI ID @code{0x27a28086}) and Intel +965GM (PCI ID @code{0x2a028086}). + +@node font_module +@section font +This module provides support for the commands @command{loadfont} and +@command{lsfonts} to load a given font or list the loaded fonts. @xref{loadfont} +and @pxref{lsfonts} for more information. + +@node freedos_module +@section freedos +This module provides support for command @command{freedos} for loading a FreeDOS +kernel. + +@node fshelp_module +@section fshelp +This module provides support functions (helper functions) for file systems. + +@node functional_test_module +@section functional_test +This module provides support for running the GRUB functional tests using +commands @command{functional_test} and @command{all_functional_test}. + +@node gcry_arcfour_module +@section gcry_arcfour +This module provides support for the arcfour stream cipher also known as RC4. +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_blowfish_module +@section gcry_blowfish +This module provides support for the Blowfish cipher. +This GRUB module is based on libgcrypt. + +@node gcry_camellia_module +@section gcry_camellia +This module provides support for the Camellia cipher. +This GRUB module is based on libgcrypt. + +@node gcry_cast5_module +@section gcry_cast5 +This module provides support for the CAST5 (RFC2144, also known as CAST-128) +cipher. This GRUB module is based on libgcrypt. + +@node gcry_crc_module +@section gcry_crc +This module provides support for the CRC32, CRC32 RFC1510, and CRC24 RFC2440 +cyclic redundancy checks. +This GRUB module is based on libgcrypt. + +@node gcry_des_module +@section gcry_des +This module provides support for the Data Encryption Standard (DES) and +Triple-DES ciphers. +If security is a concern, DES has known vulnerabilities and is not recommended, +and Triple-DES is no longer recommended by NIST. +This GRUB module is based on libgcrypt. + +@node gcry_dsa_module +@section gcry_dsa +This module provides support for the Digital Signature Algorithm (DSA) cipher. +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_md4_module +@section gcry_md4 +This module provides support for the Message Digest 4 (MD4) message digest. +If security is a concern, MD4 has known vulnerabilities and is not recommended. +This GRUB module is based on libgcrypt. + +@node gcry_md5_module +@section gcry_md5 +This module provides support for the Message Digest 5 (MD5) message digest. +If security is a concern, MD5 has known vulnerabilities and is not recommended. +This GRUB module is based on libgcrypt. + +@node gcry_rfc2268_module +@section gcry_rfc2268 +This module provides support for the RFC2268 (RC2 / Ron's Cipher 2) cipher. +If security is a concern, RC2 has known vulnerabilities and is not recommended. +This GRUB module is based on libgcrypt. + +@node gcry_rijndael_module +@section gcry_rijndael +This module provides support for the Advanced Encryption Standard (AES-128, +AES-192, and AES-256) ciphers. +This GRUB module is based on libgcrypt. + +@node gcry_rmd160_module +@section gcry_rmd160 +This module provides support for the RIPEMD-160 message digest. +This GRUB module is based on libgcrypt. + +@node gcry_rsa_module +@section gcry_rsa +This module provides support for the Rivest–Shamir–Adleman (RSA) cipher. +This GRUB module is based on libgcrypt. + +@node gcry_seed_module +@section gcry_seed +This module provides support for the SEED cipher. +This GRUB module is based on libgcrypt. + +@node gcry_serpent_module +@section gcry_serpent +This module provides support for the Serpent (128, 192, and 256) ciphers. +This GRUB module is based on libgcrypt. + +@node gcry_sha1_module +@section gcry_sha1 +This module provides support for the Secure Hash Algorithm 1 (SHA-1) message +digest. +If security is a concern, SHA-1 has known vulnerabilities and is not +recommended. +This GRUB module is based on libgcrypt. + +@node gcry_sha256_module +@section gcry_sha256 +This module provides support for the Secure Hash Algorithm 2 (224 and 256 bit) +(SHA-224 / SHA-256) message digests. +This GRUB module is based on libgcrypt. + +@node gcry_sha512_module +@section gcry_sha512 +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_tiger_module +@section gcry_tiger +This module provides support for the Tiger, Tiger 1, and Tiger 2 message +digests. +This GRUB module is based on libgcrypt. + +@node gcry_twofish_module +@section gcry_twofish +This module provides support for the Twofish (128 and 256) ciphers. +This GRUB module is based on libgcrypt. + +@node gcry_whirlpool_module +@section gcry_whirlpool +This module provides support for the Whirlpool message digest. +This GRUB module is based on libgcrypt. + +@node gdb_module +@section gdb +This module provides support for remotely debugging GRUB using the GNU +Debugger (GDB) over serial. This is typically done when troubleshooting GRUB +during development and not required for normal GRUB operation. This module adds +support for commands required by the GDB remote debug function including +@command{gdbstub} to start GDB stub on given serial port, +@command{gdbstub_break} to break into GDB, @command{gdbstub_stop} to stop the +GDB stub. + +@node geli_module +@section geli +This module provides support for the GEOM ELI (GELI) disk encryption / +decryption protocol used by FreeBSD. This module supports the following ciphers +using the associated "gcry" modules: DES, Triple-DES, Blowfish, CAST5, AES, and +Camellia 128. + +@node gettext_module +@section gettext +This module provides support for the @command{gettext} command to support +translating information displayed / output by GRUB. @xref{gettext} for more +information. + +@node gfxmenu_module +@section gfxmenu +This module provides support for displaying a graphical menu / user interface +from GRUB. This includes features such as graphical font support, theme support, +image support, and icon support. + +@node gfxterm_module +@section gfxterm +This module provides support for displaying a graphical terminal interface from +GRUB. + +@node gfxterm_background_module +@section gfxterm_background +This module provides support for setting the gfxterm background color and +background image using commands @command{background_color} and +@command{background_image}. @xref{background_color} and @pxref{background_image} +for more information. + +@node gfxterm_menu_module +@section gfxterm_menu +This module is intended for performing a functional test of the gfxmenu function +in GRUB. + +@node gptsync_module +@section gptsync +This module provides support for the @command{gptsync} command.. @xref{gptsync} +for more information. + +@node gzio_module +@section gzio +This module provides support for decompression (inflate) of files compressed +with the GZ compression algorithm. This supports only the "DEFLATE" method for +GZIP. Unsupported flags (will result in failure to inflate) include: +@code{GRUB_GZ_CONTINUATION}, @code{GRUB_GZ_ENCRYPTED}, +@code{GRUB_GZ_RESERVED}, and @code{GRUB_GZ_EXTRA_FIELD}. + +@node halt_module +@section halt +This module provides support for the @command{halt} command to shutdown / halt +the system. @xref{halt} for more information. + +@node hashsum_module +@section hashsum +This module provide support for the commands @command{hashsum}, +@command{md5sum}, @command{sha1sum}, @command{sha256sum}, @command{sha512sum}, +and @command{crc} to calculate or check hashes of files using various methods. +@xref{hashsum}, @pxref{md5sum} @pxref{sha1sum}, @pxref{sha256sum}, +@pxref{sha512sum}, and @pxref{crc}. + +@node hdparm_module +@section hdparm +This module provides support for the @command{hdparm} command to get or set +various ATA disk parameters. This includes controlling Advanced Power Management +(APM), displaying power mode, freezing ATA security settings until reset, +displaying SMART status, controlling automatic acoustic management, setting +standby timeout, setting the drive to standby mode, setting the drive to sleep +mode, displaying the drive identification and settings, and enable/disable +SMART. + +@node hello_module +@section hello +This provides support for the @command{hello} command to simply output +"Hello World". This is intended for testing GRUB module loading / functionality. + +@node help_module +@section help +This module provides support for the @command{help} command to output help +text. @xref{help} for more information. + +@node hexdump_module +@section hexdump +This module provides support for the @command{hexdump} command to dump the +contents of a file in hexadecimal. @xref{hexdump} for more information. + +@node hfs_module +@section hfs +This module provides support for the Hierarchical File System (HFS) file system +in GRUB. + +@node hfsplus_module +@section hfsplus +This module provides support for the Hierarchical File System Plus (HFS+) file +system in GRUB. + +@node hfspluscomp_module +@section hfspluscomp +This module provides support for the Hierarchical File System Plus Compressed +(HFS+ Compressed) file system in GRUB. + +@node http_module +@section http +This module provides support for getting data over the HTTP network protocol in +GRUB (using the HTTP GET method). This may be used, for example, to obtain +an operating system over HTTP (network boot). + +@node ieee1275_fb_module +@section ieee1275_fb +This module provides support for the IEEE1275 video driver output for PowerPC +with a IEEE-1275 platform. + +@node iorw_module +@section iorw +This module provides support for commands @command{inb}, @command{inw}, +@command{inl}, @command{outb}, @command{outw}, and @command{outl} to read / +write data to physical I/O ports. The "in" commands accept one +parameter to specify the source port. The "out" commands require either two +or three parameters, with the order: port, value, . + +@node iso9660_module +@section iso9660 +This module provides support for the ISO9660 file system (often associated with +optical disks such as CD-ROMs and DVD-ROMs, with extensions: +System Use Sharing Protocol (SUSP), Rock Ridge (UNIX style permissions and +longer names) + +@node jfs_module +@section jfs +This module provides support for the Journaled File System (JFS) file system. + +@node jpeg_module +@section jpeg +This module provides support for reading JPEG image files in GRUB, such as +to support displaying a JPEG image as a background image of the gfxmenu. + +@node json_module +@section json +This module provides library support for parsing / processing JavaScript Object +Notation (JSON) formatted data. This is used, for example, to support LUKS2 +disk encryption / decryption as metadata is encoded in JSON. + +@node keylayouts_module +@section keylayouts +This module provides support for the @command{keymap} command. This command +accepts one parameter to specify either the @var{layout_name} or the +@var{filename}. +When specifying the @var{layout_name}, this command will attempt to open the +GRUB keymap file based on the following logic: + +Get the "prefix" from environment variable @var{prefix} + +Open keymap file @var{prefix}/layouts/@var{layout_name}.gkb + +When specifying the @var{filename}, the full path to the ".gkb" file should be +provided. The ".gkb" file can be generated by grub-kbdcomp. + +@node keystatus_module +@section keystatus +This module provides support for the @command{keystatus} command to check key +modifier status. @xref{keystatus} for more information. + +@node ldm_module +@section ldm +This module provides support for the Logical Disk Manager (LDM) disk format. +LDM is used to add support for logical volumes most often with Microsoft +Windows systems. A logical volume can be defined to span more than one physical +disk. + +@node legacy_password_test_module +@section legacy_password_test +This module is intended for performing a functional test of the legacy password +function in GRUB. + +@node legacycfg_module +@section legacycfg +This module provides support for commands @command{legacy_source}, +@command{legacy_configfile}, @command{extract_legacy_entries_source}, +@command{extract_legacy_entries_configfile}, @command{legacy_kernel}, +@command{legacy_initrd}, @command{legacy_initrd_nounzip}, +@command{legacy_password}, and @command{legacy_check_password}. For new uses / +configurations of GRUB other commands / modules offer the modern equivalents. + +@node linux_module +@section linux +This module provides support for the commands @command{linux} and +@command{initrd} to load Linux and an Initial RAM Disk respectively. +@xref{linux} and @pxref{initrd} for more information. + +@node linux16_module +@section linux16 +This module provides support for the commands @command{linux16} and +@command{initrd16} to load Linux in 16-bit mode and an Initial RAM Disk +in 16-bit mode respectively. +@xref{linux16} and @pxref{initrd16} for more information. + +@node loadbios_module +@section loadbios +This module provides support for the commands @command{fakebios} and +@command{loadbios}. These commands may only be useful on platforms with +issues requiring work-arounds. Command @command{fakebios} is used to create +BIOS-like structures for backward compatibility with existing OS. Command +@command{loadbios} is used to load a BIOS dump. + +@node loadenv_module +@section loadenv +This module provides support for commands @command{load_env}, +@command{list_env}, and @command{save_env}. These commands can be used to +load environment variables from a file, list environment variables in a file, +and save environment variables to a file. @xref{load_env}, @pxref{list_env}, and +@pxref{save_env}. + +@node loopback_module +@section loopback +This module provides support for the @command{loopback} command. +@xref{loopback} for more information. + +@node ls_module +@section ls +This module provides support for the @command{ls} command. +@xref{ls} for more information. + +@node lsacpi_module +@section lsacpi +This module provides support for the @command{lsacpi} command. This command +can be used to display Advanced Configuration and Power Interface (ACPI) tables. + +@node lsapm_module +@section lsapm +This module provides support for the @command{lsapm} command. This command +can be used to display Advanced power management (APM) information. + +@node lsdev_module +@section lsdev +This module provides support for the @command{lsdev} command. This command +can be used on MIPS Advanced RISC Computing (ARC) platforms to display devices. + +@node lsefi_module +@section lsefi +This module provides support for the @command{lsefi} command. This command +can be used on EFI platforms to display EFI handles. + +@node lsefimmap_module +@section lsefimmap +This module provides support for the @command{lsefimmap} command. This command +can be used on EFI platforms to display the EFI memory map. + +@node lsefisystab_module +@section lsefisystab +This module provides support for the @command{lsefisystab} command. This +command can be used on EFI platforms to display the EFI system tables. + +@node lsmmap_module +@section lsmmap +This module provides support for the @command{lsmmap} command. This +command can be used to display the memory map provided by firmware. + +@node lspci_module +@section lspci +This module provides support for the @command{lspci} command. This +command can be used to display the PCI / PCIe devices. + +@node lssal_module +@section lssal +This module provides support for the @command{lsefisystab} command. This +command can be used on Itanium (IA-64) EFI platforms to display the EFI +System Abstraction Layer system table. + +@node lsspd_module +@section lsspd +This module provides support for the @command{lsspd} command. This +command can be used on MIPS Loongson platforms to display the DDR RAM Serial +Presence Detect (SPD) EEPROM data. + +@node lsxen_module +@section lsxen +This module provides support for the commands @command{xen_ls} and +@command{xen_cat} on Xen platforms to list Xen storage. + +@node luks_module +@section luks +This module provides support for the Linux Unified Key Setup (LUKS) (version 1) +disk encryption / decryption protocol. + +@node luks2_module +@section luks2 +This module provides support for the Linux Unified Key Setup 2 (LUKS2) +disk encryption / decryption protocol. + +@node lvm_module +@section lvm +This module provides support for reading Logical Volume Management "logical" +disks. For example, a single "logical" disk may be mapped to span more than one +physical disk. This would be used when booting from a LVM formatted disk as may +be setup in Linux. + +@node lzopio_module +@section lzopio +This module provides support for decompressing LZO / LZOP compressed files / +archives. + +@node macbless_module +@section macbless +This module provides support for commands @command{mactelbless} and +@command{macppcbless} for "blessing" a bootloader on Intel / PPC based MACs +using the HFS or HFS+ file system. On HFS / HFS+ - "blessing" makes a file +run as the bootloader. + +@node macho_module +@section macho +This module provides support for Mach Object (Mach-O) object / executable files +in GRUB often used in MacOS. + +@node mda_text_module +@section mda_text +This module provides support for the Monochrome Display Adapter (MDA) terminal +output device. MDA is a predecessor to VGA. + +@node mdraid09_module +@section mdraid09 +This module provides support for handling Linux compatible "version 0.9" +software-based RAID disks in little-endian format. The "version 0.9" format +was largely replaced around the year 2009 with the "version 1.x" format +(@pxref{mdraid1x_module} for more information). + +@node mdraid09_be_module +@section mdraid09_be +This module provides support for handling Linux compatible "version 0.9" +software-based RAID disks in bid-endian format. The "version 0.9" format +was largely replaced around the year 2009 with the "version 1.x" format +(@pxref{mdraid1x_module} for more information). + +@node mdraid1x_module +@section mdraid1x +This module provides support for handling Linux compatible "version 1.x" +software-based RAID disks. This includes the current version used by Linux +at the time of writing. + +@node memdisk_module +@section memdisk +This module provides support for a memdisk device. A memdisk is a memory mapped +emulated disk likely only possible in legacy environment such as with a legacy +BIOS operating in 16-bit mode. + +@node memrw_module +@section memrw +This module provides support for commands @command{read_byte}, +@command{read_word}, @command{read_dword}, @command{write_byte}, +@command{write_word}, and @command{write_dword} to read / +write data to physical memory (addresses). The "read" commands accept one +parameter to specify the source address. The "write" commands require either two +or three parameters, with the order: address, value, . + +@node memtools_module +@section memtools +This module provides support for GRUB development / debugging commands +@command{lsmem}, @command{lsfreemem}, and @command{stress_big_allocs}. + +@node minicmd_module +@section minicmd +This module provides support for a subset of commands for GRUB rescue mode +including: @command{cat}, @command{help}, @command{dump}, @command{rmmod}, +@command{lsmod}, and @command{exit}. The version of the commands in this module +are similar to their full-fledged counterparts implemented in other GRUB +modules. + +@node minix_module +@section minix +This module provides support for the Minix filesystem, version 1. + +@node minix2_module +@section minix2 +This module provides support for the Minix filesystem, version 2. + +@node minix2_be_module +@section minix2_be +This module provides support for the Minix filesystem, version 2 big-endian. + +@node minix3_module +@section minix3 +This module provides support for the Minix filesystem, version 3. + +@node minix3_be_module +@section minix3_be +This module provides support for the Minix filesystem, version 3 big-endian. + +@node minix_be_module +@section minix_be +This module provides support for the Minix filesystem, version 1 big-endian. + +@node mmap_module +@section mmap +This module provides support for mapping or unmapping devices or files into +memory as well as commands @command{badram} and @command{cutmem}. +@xref{badram} and @ref{cutmem}. + +@node morse_module +@section morse +This module provides support for outputting terminal output via Morse code +to an audio speaker output. + +@node mpi_module +@section mpi +This module provides support for multi-precision-integers (MPIs) in GRUB. MPIs +are used by the crypto functions as many depend on mathematics of large numbers. +This GRUB module is based on libgcrypt. + +@node msdospart_module +@section msdospart +This module provides support for modifying MSDOS formatted disk partitions +through the separate @command{parttool} command. + +@node mul_test_module +@section mul_test +This module is intended for performing a functional test of the multiplication +operations in GRUB. + +@node multiboot_module +@section multiboot +This module provides support for commands @command{multiboot} and +@command{module} to load a multiboot kernel and load a multiboot module, +respectively. @xref{multiboot} and @ref{module} for more information. This +is for loading data formatted per the GNU Multiboot specification. + +@node multiboot2_module +@section multiboot2 +This module provides support for commands @command{multiboot2} and +@command{module2} to load a multiboot kernel and load a multiboot module, +respectively. This is for loading data formatted per the GNU Multiboot +specification. + +@node nand_module +@section nand +This module provides support for accessing an IEEE-1275 compliant NAND disk +from GRUB. + +@node nativedisk_module +@section nativedisk +This module provides support for the @command{nativedisk} command. +@xref{nativedisk} for more information. + +@node net_module +@section net +This module provides support for networking protocols including ARP, BOOTP, +DNS, Ethernet, ICMPv6, ICMP, IP, TCP, and UDP. Support is included for both +IPv4 and IPv6. +This includes the following commands: +@itemize @bullet +@item +@command{net_bootp} - @pxref{net_bootp} + +@item +@command{net_dhcp} - @pxref{net_dhcp} + +@item +@command{net_get_dhcp_option} - @pxref{net_get_dhcp_option} + +@item +@command{net_nslookup} - @pxref{net_nslookup} + +@item +@command{net_add_dns} - @pxref{net_add_dns} + +@item +@command{net_del_dns} - @pxref{net_del_dns} + +@item +@command{net_ls_dns} - @pxref{net_ls_dns} + +@item +@command{net_add_addr} - @pxref{net_add_addr} + +@item +@command{net_ipv6_autoconf} - @pxref{net_ipv6_autoconf} + +@item +@command{net_del_addr} - @pxref{net_del_addr} + +@item +@command{net_add_route} - @pxref{net_add_route} + +@item +@command{net_del_route} - @pxref{net_del_route} + +@item +@command{net_set_vlan} - @pxref{net_set_vlan} + +@item +@command{net_ls_routes} - @pxref{net_ls_routes} + +@item +@command{net_ls_cards} - @pxref{net_ls_cards} + +@item +@command{net_ls_addr} - @pxref{net_ls_addr} + +@end itemize + +@node newc_module +@section newc +This module provides support for accessing a CPIO archive as a file system +from GRUB. This module is for the following newer variants of the CPIO archive +supported by GNU CPIO (but GNU CPIO defaults to the "bin" format which is +handled by the module @ref{cpio_module}). + +These are the variants supported by this module: + +@itemize @bullet +@item +"newc" - SVR4 portable format without CRC. GNU file utility will identify these +as something like "ASCII cpio archive (SVR4 with no CRC)" + +@item +‘crc’ - SVR4 portable format with CRC. GNU file utility will identify these as +something like "ASCII cpio archive (SVR4 with CRC)" + +@end itemize + +@node nilfs2_module +@section nilfs2 +This module provides support for the New Implementation of Log filesystem +(nilfs2). + +@node normal_module +@section normal +This module provides support for the normal mode in GRUB. @xref{normal} for +more information. + +@node ntfs_module +@section ntfs +This module provides support for the New Technology File System (NTFS) in GRUB. + +@node ntfscomp_module +@section ntfscomp +This module provides support for compression with the New Technology File +System (NTFS) in GRUB. + +@node ntldr_module +@section ntldr +This module provides support for the @command{ntldr} command. This is may be +used to boot a Windows boot loader such as NTLDR or BootMGR. + +@node odc_module +@section odc +This module provides support for accessing a CPIO archive as a file system +from GRUB. This module is for "odc" variant of the CPIO archive +supported by GNU CPIO (but GNU CPIO defaults to the "bin" format which is +handled by the module @ref{cpio_module}). + +GNU file utility will identify these as something like "ASCII cpio archive +(pre-SVR4 or odc)" + +@node offsetio_module +@section offsetio +This module provides support for reading from a file / archive at specified +offsets in GRUB. + +@node ofnet_module +@section ofnet +This module provides support for the Open Firmware (IEEE-1275) network device +support in GRUB. + +@node ohci_module +@section ohci +This module provides support for the Open Host Controller Interface (OHCI) for +USB 1 / USB 1.1 support in GRUB. + +@node part_acorn_module +@section part_acorn +This module provides support for reading from disks partitioned with the +Acorn Disc Filing System (ADFS) used on RiscOS. + +@node part_amiga_module +@section part_amiga +This module provides support for reading from disks partitioned with the +Amiga partition table. + +@node part_apple_module +@section part_apple +This module provides support for reading from disks partitioned with the +Macintosh partition table. + +@node part_bsd_module +@section part_bsd +This module provides support for reading from disks partitioned with BSD +style partition tables. + +@node part_dfly_module +@section part_dfly +This module provides support for reading from disks partitioned with the +DragonFly BSD partition table. + +@node part_dvh_module +@section part_dvh +This module provides support for reading from disks partitioned with the +SGI Disk Volume Header partition table. + +@node part_gpt_module +@section part_gpt +This module provides support for reading from disks partitioned with the +GUID Partition Tables (GPT) partition table. + +@node part_msdos_module +@section part_msdos +This module provides support for reading from disks partitioned with the +MSDOS (Master Boot Record / MBR) style partition tables. + +@node part_plan_module +@section part_plan +This module provides support for reading from disk partitioned with the +Plan9 style partition table. + +@node part_sun_module +@section part_sun +This module provides support for reading from disk partitioned with the +Sun style partition table. + +@node part_sunpc_module +@section part_sunpc +This module provides support for reading from disk partitioned with the +Sun PC style partition table. + +@node parttool_module +@section parttool +This module provides support for the @command{parttool} command. @xref{parttool} +for more information. + +@node password_module +@section password +This module provides support for the @command{password} command. Please note +that this uses the password in plain text, if security is a concern consider +using @ref{password_pbkdf2_module} instead. @xref{password} for more +information. + +@node password_pbkdf2_module +@section password_pbkdf2 +This module provides support for the @command{password_pbkdf2} command. +@xref{password_pbkdf2} for more information. + +@node pata_module +@section pata +This module provides support for Parallel ATA (PATA) disk device interfaces. + +@node pbkdf2_module +@section pbkdf2 +This module provides support for the Password-Based Key Derivation Function 2 +(PBKDF2) / PKCS#5 PBKDF2 as per RFC 2898. + +@node pbkdf2_test_module +@section pbkdf2_test +This module is intended for performing a functional test of the PBKDF2 +operation in GRUB. + +@node pci_module +@section pci +This module provides support for generic Peripheral Component Interconnect (PCI) +bus in GRUB. + +@node pcidump_module +@section pcidump +This module provides support for the @command{pcidump} command in GRUB to dump +the PCI configuration registers in hexadecimal of a specified PCI device +(vendor / device ID) or by position on the bus. + +@node pgp_module +@section pgp +This module provides support for the commands: @command{verify_detached}, +@command{trust}, @command{list_trusted}, @command{distrust} associated with +digital signature checking via the "Open Pretty Good Privacy" (PGP) protocol / +RFC 4880 using a provided public key. This module also uses / sets +environment variable @code{check_signatures}. @xref{verify_detached}, +@ref{trust}, @ref{list_trusted}, @ref{distrust}, and @ref{check_signatures}. + +@node plainmount_module +@section plainmount +This module provides support for accessing / mounting partitions encrypted +by "cryptsetup" operating in "plain mode". @xref{plainmount} for more +information. + +@node plan9_module +@section plan9 +This module provides support for the @command{plan9} command to load a Plan9 +kernel. + +@node play_module +@section play +This module provides support for the @command{play} command to play a tune +through the PC speaker. @xref{play} for more information. + +@node png_module +@section png +This module provides support for reading Portable Network Graphics (PNG) image +files in GRUB. + +@node priority_queue_module +@section priority_queue +This module provides support for a priority queue function within GRUB such as +to support networking functions. + +@node probe_module +@section probe +This module provides support for the @command{probe} command to retrieve device +information. @xref{probe} for more information. + +@node procfs_module +@section procfs +This module provides support for a Proc File System to provide a file system +like interface to some GRUB internal data. + +@node progress_module +@section progress +This module provides support for showing file loading progress to the +terminal. + +@node pxe_module +@section pxe +This module provides support for Preboot Execution Environment (PXE) network +boot services as a file system driver for other GRUB modules. + +@node pxechain_module +@section pxechain +This module provides support for the @command{pxechainloader} command to load +another bootloader by PXE. + +@node raid5rec_module +@section raid5rec +This module provides support for recovering from faulty RAID4/5 disk arrays + +@node raid6rec_module +@section raid6rec +This module provides support for recovering from faulty RAID6 disk arrays. + +@node random_module +@section random +This module provides support for library functions to get random data via +the hardware ACPI Power Management Timer and the TSC time source (Timestamp +Counter). + +@node rdmsr_module +@section rdmsr +This module provides support for the @command{rdmsr} command to read CPU +Model Specific Registers. @xref{rdmsr} for more information. + +@node read_module +@section read +This module provides support for the @command{read} command for getting user +input. @xref{read} for more information. + +@node reboot_module +@section reboot +This module provides support for the @command{reboot} command to reboot the +computer. @xref{reboot} for more information. + +@node regexp_module +@section regexp +This module provides support for the @command{regexp} command to check if a +regular expression matches a string. This module also provides support for the +GRUB script wildcard translator. @xref{regexp} for more information. + +@node reiserfs_module +@section reiserfs +This module provides support for the ReiserFS File System in GRUB. + +@node relocator_module +@section relocator +This module provides support for relocating the image / executable being loaded +to the expected memory location(s) and jumping to (invoking) the executable. + +@node romfs_module +@section romfs +This module provides support for the Read-Only Memory File System (ROMFS). + +@node scsi_module +@section scsi +This module provides support for the Small Computer System Interface (SCSI) +protocol used for some types of disk communication include some modern ones +such as USB Mass Storage Devices supporting "USB Attached SCSI" (UAS). + +@node sdl_module +@section sdl +This module provides support for Simple DirectMedia Layer (SDL) video / image +output from the grub-emu tool used to preview the GRUB menu from a running +Operating System such as Linux (useful to test GRUB menu configuration changes +without rebooting). When available in the compilation target environment, SDL2 +will be used instead of SDL1. + +@node search_module +@section search +This module provides support for the @command{search} command to search devices +by file, filesystem label, or filesystem UUID. @xref{search} for more +information. + +@node search_fs_file_module +@section search_fs_file +This module provides support for the @command{search.file} command which +is an alias for the corresponding @command{search} command. @xref{search} for +more information. + +@node search_fs_uuid_module +@section search_fs_uuid +This module provides support for the @command{search.fs_uuid} command which +is an alias for the corresponding @command{search} command. @xref{search} for +more information. + +@node search_label_module +@section search_label +This module provides support for the @command{search.fs_label} command which +is an alias for the corresponding @command{search} command. @xref{search} for +more information. + +@node sendkey_module +@section sendkey +This module provides support for the @command{sendkey} command to send +emulated keystrokes. @xref{sendkey} for more information. + +@node serial_module +@section serial +This module provides support for the @command{serial} command and associated +driver support for communication over a serial interface from GRUB. +@xref{serial} for more information. + +@node setjmp_module +@section setjmp +This module provides support for the @code{setjmp} and @code{longjmp} functions +used within GRUB. + +@node setjmp_test_module +@section setjmp_test +This module is intended for performing a functional test of the @code{setjmp} +and @code{longjmp} functions in GRUB. + +@node setpci_module +@section setpci +This module provides support for the @command{setpci} command to get / set +values from / to specified PCI / PCIe devices. + +@node sfs_module +@section sfs +This module provides support for the Amiga Smart File System (SFS) in GRUB. + +@node shift_test_module +@section shift_test +This module is intended for performing a functional test of the bit-wise shift +operations in GRUB. + +@node signature_test_module +@section signature_test +This module is intended for performing a functional test of the digital +signature verification functions in GRUB. + +@node sleep_module +@section sleep +This module provides support for the @command{sleep} command to wait a specified +number of seconds in GRUB. @xref{sleep} for more information. + +@node sleep_test_module +@section sleep_test +This module is intended for performing a functional test of the sleep function +in GRUB. + +@node smbios_module +@section smbios +This module provides support for the @command{smbios} command to retrieve SMBIOS +information in GRUB. @xref{smbios} for more information. + +@node spkmodem_module +@section spkmodem +This module provides support for outputting GRUB console information over an +audio output. This output can be fed into another computer's sound input +and decoded using the @code{spkmodem_recv} utility. Note that this will slow +down GRUB's performance. + +@node squash4_module +@section squash4 +This module provides support for the SquashFS compressed read-only file system +in GRUB. + +@node strtoull_test_module +@section strtoull_test +This module is intended for performing a functional test of the strtoull +function in GRUB. + +@node suspend_module +@section suspend +This module provides support for the @command{suspend} command in GRUB to +return to IEEE1275 prompt on "Open Firmware" systems. + +@node syslinuxcfg_module +@section syslinuxcfg +This module provides support for commands @command{syslinux_source}, +@command{syslinux_configfile}, @command{extract_syslinux_entries_source}, +and @command{extract_syslinux_entries_configfile} in GRUB. These commands +can be used to parse and display GRUB menu entries based on a Syslinux based +configuration (used for SYSLINUX, ISOLINUX, and PXELINUX). It can also +be used to execute the Syslinux loader from GRUB. + +@node tar_module +@section tar +This module provides support for the GNU Tar and POSIX Tar file archives as a +file system in GRUB. + +@node terminal_module +@section terminal +This module provides support for the commands @command{terminal_input} and +@command{terminal_output} in GRUB. @xref{terminal_input} and +@ref{terminal_output} for more information. + +@node terminfo_module +@section terminfo +This module provides support for the @command{terminfo} command in GRUB to +set various terminal modes / options. @xref{terminfo} for more information. + +@node test_module +@section test +This module provides support for the commands @command{test} and @command{[}. +These commands can be used to evaluate (test) an expression. @xref{test} for +more information. + +@node test_blockarg_module +@section test_blockarg +This module is intended for performing a functional test of the "block" command +argument function in GRUB internal functions via a test command +@command{test_blockarg}. + +@node testload_module +@section testload +This module is intended for performing a functional test of some file reading / +seeking functions in GRUB internals via a test command @command{testload}. + +@node testspeed_module +@section testspeed +This module provides support for the @command{testspeed} command to test and +print file read speed of a specified file. + +@node tftp_module +@section tftp +This module provides support for the Trivial File Transfer Protocol (TFTP) for +receiving files via the network to GRUB. TFTP may be used along with PXE for +network booting for example. + +@node tga_module +@section tga +This module provides support for reading Truevision Graphics Adapter (TGA) +image files in GRUB. + +@node time_module +@section time +This module provides support for the @command{time} command to measure the +time taken by a given command and output it to the terminal. + +@node tpm_module +@section tpm +This module provides support for interacting with a Trusted Platform Module +(TPM) with GRUB to perform Measured Boot. @xref{Measured Boot} for more +information. + +@node tr_module +@section tr +This module provides support for the @command{tr} command in GRUB. This can be +used to translate characters in a string according to the provided arguments. +For example this can be used to convert upper-case to lower-case and visa-versa. + +@node trig_module +@section trig +This module provides support for internal trig functions @code{grub_cos} and +@code{grub_sin} using lookup based computation. Currently these trig functions +are used by the gfxmenu circular progress bar. + +@node true_module +@section true +This module provides support for the commands @command{true} and +@command{false}. @xref{true} and @ref{false} for more information. + +@node truecrypt_module +@section truecrypt +This module provides support for the @command{truecrypt} command. This can be +used to load a Truecrypt ISO image. + +@node ubootnet_module +@section ubootnet +This module provides support for configuring network interfaces in GRUB using +information provided by a U-Boot bootloader. + +@node udf_module +@section udf +This module provides support for the Universal Disk Format (UDF) used on some +newer optical disks. + +@node ufs1_module +@section ufs1 +This module provides support for the Unix File System version 1 in GRUB. + +@node ufs1_be_module +@section ufs1_be +This module provides support for the Unix File System version 1 (big-endian) in +GRUB. + +@node ufs2_module +@section ufs2 +This module provides support for the Unix File System version 2 in GRUB. + +@node uhci_module +@section uhci +This module provides support for the Universal Host Controller Interface (UHCI) +for USB 1.x. + +@node usb_module +@section usb +This module provides support for USB interfaces, USB hubs, and USB transfers +in GRUB. + +@node usb_keyboard_module +@section usb_keyboard +This module provides support for a USB keyboard in GRUB. + +@node usbms_module +@section usbms +This module provides support for USB Mass Storage devices in GRUB. + +@node usbserial_common_module +@section usbserial_common +This module provides support for common operations needed to support USB Serial +port adapters in GRUB (to support a model / type specific USB to serial +adapter defined in another module). + +@node usbserial_ftdi_module +@section usbserial_ftdi +This module provides support for USB to serial adapters with vendor ID 0x0403 +and product ID 0x6001 (often associated with FTDI devices). + +@node usbserial_pl2303_module +@section usbserial_pl2303 +This module provides support for USB to serial adapters with vendor ID 0x067b +and product ID 0x2303 (PL2303 USB to Serial adapter). + +@node usbserial_usbdebug_module +@section usbserial_usbdebug +This module provides support for debugging GRUB via a "USB 2.0 Debug Cable". +The USB 2.0 specification includes a "USB2 Debug Device Functional +Specification" that this driver is intended to support for GRUB. This may +integrate with GDB server function in GRUB (@pxref{gdb_module}). + +@node usbtest_module +@section usbtest +This module provides support for the @command{usb} command in GRUB to test USB +functionality by iterating through all connected USB devices and printing +information for each to the terminal. + +@node vbe_module +@section vbe +This module provides support for the VESA BIOS Extension (VBE) Video Driver in +GRUB. + +@node verifiers_module +@section verifiers +This module is a built-in kernel module to provide a framework for GRUB file +verifiers and string verifiers. + +@node vga_module +@section vga +This module provides support for the Video Graphics Array (VGA) Video Driver in +GRUB. + +@node vga_text_module +@section vga_text +This module provides support for the Video Graphics Array (VGA) terminal +output device. + +@node video_module +@section video +This module provides support for video output support functions within GRUB. + +@node video_bochs_module +@section video_bochs +This module provides support for the Bochs PCI Video Driver (also known as +Bochs Graphics Adapter / BGA) in GRUB. + +@node video_cirrus_module +@section video_cirrus +This module provides support for the Cirrus CLGD 5446 PCI Video Driver (Cirrus +Video) in GRUB. + +@node video_colors_module +@section video_colors +This module provides support for interpreting named colors and parsing RBG +hexadecimal values. + +@node video_fb_module +@section video_fb +This module provides support for video frame buffer (FB) support in GRUB. + +@node videoinfo_module +@section videoinfo +This module provides support for the @command{videoinfo} command and (depending +on architecture) the @command{vbeinfo} command. @xref{videoinfo} for more +information. + +@node videotest_module +@section videotest +This module provides support for the @command{videotest} command and (depending +on architecture) the @command{vbetest} to test the video subsystem in the +specified width and height. + +@node videotest_checksum_module +@section videotest_checksum +This module is intended for performing a functional test of the video +functions in GRUB by displaying a test image and capturing a checksum. + +@node wrmsr_module +@section wrmsr +This module provides support for the @command{wrmsr} command to write to CPU +model-specific registers. @xref{wrmsr} for more information. + +@node xen_boot_module +@section xen_boot +This module provides support for the commands @command{xen_hypervisor} and +@command{xen_module} to load a XEN hypervisor and module respectively. + +@node xfs_module +@section xfs +This module provides support for the XFS file system in GRUB. + +@node xnu_module +@section xnu +This module provides support for the commands: @command{xnu_devprop_load}, +@command{xnu_kernel}, @command{xnu_kernel64}, @command{xnu_mkext}, +@command{xnu_kext}, @command{xnu_kextdir}, @command{xnu_ramdisk}, +@command{xnu_splash}, and @command{xnu_resume} (only for emulated machine). +These commands support loading and interacting with a XNU (MacOS / Apple) based +system / kernel. + +@node xnu_uuid_module +@section xnu_uuid +This module provides support for the @command{xnu_uuid} command to transform +a 64-bit UUID to a format suitable for XNU. + +@node xnu_uuid_test_module +@section xnu_uuid_test +This module is intended for performing a functional test of the XNU UUID +conversion function. + +@node xzio_module +@section xzio +This module provides support for decompression of XZ compressed data. + +@node zfs_module +@section zfs +This module provides support for the ZFS file system in GRUB. + +@node zfscrypt_module +@section zfscrypt +This module provides support for the @command{zfskey} to import a decryption +key as well as decryption support for encrypted ZFS file systems. + +@node zfsinfo_module +@section zfsinfo +This module provides support for the commands @command{zfsinfo} to output ZFS +info about a device and @command{zfs-bootfs} to output ZFS-BOOTFSOBJ or store +it into a variable. + +@node zstd_module +@section zstd +This module provides support for the Zstandard (zstd) decompression algorithm +in GRUB. + @node Commands @chapter Available commands @@ -4374,6 +6396,7 @@ you forget a command, you can run the command @command{help} * export:: Export an environment variable * false:: Do nothing, unsuccessfully * fdtdump:: Retrieve device tree information +* file:: Test the provided file against a type * fwsetup:: Reboot into the firmware setup menu * gdbinfo:: Provide info for debugging with GDB * gettext:: Translate a string @@ -4930,6 +6953,94 @@ echo $machine_model @end deffn +@node file +@subsection file + +@deffn Command file is_file_type filename + +The @command{file} command tests whether the provided @var{filename} is the +type provided by @var{is_file_type}. When the @command{file} is of type +@var{is_file_type} this command will return 0, otherwise it will return +non-zero (no output is provided to the terminal). + +@var{is_file_type} may be one of the following options: +@itemize @bullet +@item +@option{--is-i386-xen-pae-domu} Check if @var{filename} can be booted as i386 +PAE Xen unprivileged guest kernel +@item +@option{--is-x86_64-xen-domu} Check if @var{filename} can be booted as x86_64 +Xen unprivileged guest kernel +@item +@option{--is-x86-xen-dom0} Check if @var{filename} can be used as Xen x86 +privileged guest kernel +@item +@option{--is-x86-multiboot} Check if @var{filename} can be used as x86 +multiboot kernel +@item +@option{--is-x86-multiboot2} Check if @var{filename} can be used as x86 +multiboot2 kernel +@item +@option{--is-arm-linux} Check if @var{filename} is ARM Linux +@item +@option{--is-arm64-linux} Check if @var{filename} is ARM64 Linux +@item +@option{--is-ia64-linux} Check if @var{filename} is IA64 Linux +@item +@option{--is-mips-linux} Check if @var{filename} is MIPS Linux +@item +@option{--is-mipsel-linux} Check if @var{filename} is MIPSEL Linux +@item +@option{--is-sparc64-linux} Check if @var{filename} is SPARC64 Linux +@item +@option{--is-powerpc-linux} Check if @var{filename} is POWERPC Linux +@item +@option{--is-x86-linux} Check if @var{filename} is x86 Linux +@item +@option{--is-x86-linux32} Check if @var{filename} is x86 Linux supporting +32-bit protocol +@item +@option{--is-x86-kfreebsd} Check if @var{filename} is x86 kFreeBSD +@item +@option{--is-i386-kfreebsd} Check if @var{filename} is i386 kFreeBSD +@item +@option{--is-x86_64-kfreebsd} Check if @var{filename} is x86_64 kFreeBSD +@item +@option{--is-x86-knetbsd} Check if @var{filename} is x86 kNetBSD +@item +@option{--is-i386-knetbsd} Check if @var{filename} is i386 kNetBSD +@item +@option{--is-x86_64-knetbsd} Check if @var{filename} is x86_64 kNetBSD +@item +@option{--is-i386-efi} Check if @var{filename} is i386 EFI file +@item +@option{--is-x86_64-efi} Check if @var{filename} is x86_64 EFI file +@item +@option{--is-ia64-efi} Check if @var{filename} is IA64 EFI file +@item +@option{--is-arm64-efi} Check if @var{filename} is ARM64 EFI file +@item +@option{--is-arm-efi} Check if @var{filename} is ARM EFI file +@item +@option{--is-riscv32-efi} Check if @var{filename} is RISC-V 32bit EFI file +@item +@option{--is-riscv64-efi} Check if @var{filename} is RISC-V 64bit EFI file +@item +@option{--is-hibernated-hiberfil} Check if @var{filename} is hiberfil.sys in +hibernated state +@item +@option{--is-x86_64-xnu} Check if @var{filename} is x86_64 XNU (Mac OS X kernel) +@item +@option{--is-i386-xnu} Check if @var{filename} is i386 XNU (Mac OS X kernel) +@item +@option{--is-xnu-hibr} Check if @var{filename} is XNU (Mac OS X kernel) +hibernated image +@item +@option{--is-x86-bios-bootsector} Check if @var{filename} is BIOS bootsector +@end itemize +@end deffn + + @node fwsetup @subsection fwsetup @@ -6223,7 +8334,6 @@ GRUB shell may provide more information on parameters and usage. @item @command{extract_syslinux_entries_configfile} - Execute syslinux config in new context taking only menu entries @item @command{extract_syslinux_entries_source} - Execute syslinux config in same context taking only menu entries @item @command{fakebios} - Create BIOS-like structures for backward compatibility with existing OS. -@item @command{file} - Check if FILE is of specified type. @item @command{fix_video} - Fix video problem. @item @command{fpswa} - Display FPSWA version. @item @command{functional_test} - Run all loaded functional tests. From 33b94f2a9bc90e086349808ef53bc7f87cff6122 Mon Sep 17 00:00:00 2001 From: Mate Kukri Date: Thu, 15 Aug 2024 10:52:56 +0100 Subject: [PATCH 170/402] loader/efi/chainloader: Do not print device path of chainloaded file Users have no reason to see this and it can break graphical boot. Signed-off-by: Mate Kukri Reviewed-by: Daniel Kiper --- grub-core/loader/efi/chainloader.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c index 1de98f783..869307bf3 100644 --- a/grub-core/loader/efi/chainloader.c +++ b/grub-core/loader/efi/chainloader.c @@ -185,7 +185,6 @@ make_file_path (grub_efi_device_path_t *dp, const char *filename) /* Fill the file path for the directory. */ d = (grub_efi_device_path_t *) ((char *) file_path + ((char *) d - (char *) dp)); - grub_efi_print_device_path (d); if (copy_file_path ((grub_efi_file_path_device_path_t *) d, dir_start, dir_end - dir_start) != GRUB_ERR_NONE) { @@ -272,9 +271,6 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), file_path = make_file_path (dp, filename); if (file_path == NULL) goto fail; - - grub_printf ("file path: "); - grub_efi_print_device_path (file_path); } size = grub_file_size (file); From 5313fa83946cae77f48be998b95550371849a017 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Tue, 3 Sep 2024 20:58:46 +0300 Subject: [PATCH 171/402] configure: Look for .otf fonts Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index d4a14bf93..fe5493246 100644 --- a/configure.ac +++ b/configure.ac @@ -1875,7 +1875,7 @@ AC_ARG_WITH([unifont], if test "x$with_unifont" = x; then # search in well-known directories - for ext in pcf pcf.gz bdf bdf.gz ttf ttf.gz; do + for ext in pcf pcf.gz bdf bdf.gz ttf ttf.gz otf otf.gz; do for dir in . /usr/src /usr/share/fonts/X11/misc /usr/share/fonts/unifont /usr/share/fonts/uni /usr/share/fonts/truetype/unifont /usr/share/fonts/misc /usr/pkg/share/fonts/X11/misc /usr/local/share/fonts/gnu-unifont /usr/local/share/fonts/unifont; do if test -f "$dir/unifont.$ext"; then md5="$(md5sum "$dir/unifont.$ext"|awk '{ print $1; }')" From e61157bbd2f5fa57883ac8fbd8d2c0fcd67fe85a Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Tue, 3 Sep 2024 20:58:47 +0300 Subject: [PATCH 172/402] fs/erofs: Replace 64-bit modulo with bitwise operations Otherwise depending on compiler we end up with umoddi3 reference and failed module dependency resolution. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/fs/erofs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/fs/erofs.c b/grub-core/fs/erofs.c index 46cfc2e5c..f2a82e988 100644 --- a/grub-core/fs/erofs.c +++ b/grub-core/fs/erofs.c @@ -357,13 +357,13 @@ erofs_map_blocks_flatmode (grub_fshelp_node_t node, { if (grub_add (erofs_iloc (node), erofs_inode_size (node), &map->m_pa) || grub_add (map->m_pa, erofs_inode_xattr_ibody_size (node), &map->m_pa) || - grub_add (map->m_pa, map->m_la % blocksz, &map->m_pa)) + grub_add (map->m_pa, map->m_la & (blocksz - 1), &map->m_pa)) return grub_error (GRUB_ERR_OUT_OF_RANGE, "m_pa overflow when handling tailpacking"); if (grub_sub (file_size, map->m_la, &map->m_plen)) return grub_error (GRUB_ERR_OUT_OF_RANGE, "m_plen overflow when handling tailpacking"); /* No overflow as map->m_plen <= UINT64_MAX - blocksz + 1. */ - if (((map->m_pa % blocksz) + map->m_plen) > blocksz) + if (((map->m_pa & (blocksz - 1)) + map->m_plen) > blocksz) return grub_error (GRUB_ERR_BAD_FS, "inline data cross block boundary @ inode %" PRIuGRUB_UINT64_T, node->ino); From f0710d2d8f4662144c7550df67ec9681e501e8c7 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Tue, 3 Sep 2024 20:58:48 +0300 Subject: [PATCH 173/402] lib/xzembed/xz_dec_bcj: Silence warning when no BCJ is available BCJ is not available for all platforms hence arguments may end up unused. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/lib/xzembed/xz_dec_bcj.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/lib/xzembed/xz_dec_bcj.c b/grub-core/lib/xzembed/xz_dec_bcj.c index bf6b5862e..aef4638d4 100644 --- a/grub-core/lib/xzembed/xz_dec_bcj.c +++ b/grub-core/lib/xzembed/xz_dec_bcj.c @@ -353,7 +353,7 @@ static noinline_for_stack size_t bcj_sparc( * avoid pointers to static data (at least on x86). */ static void bcj_apply(struct xz_dec_bcj *s, - uint8_t *buf, size_t *pos, size_t size) + uint8_t *buf __attribute__((unused)), size_t *pos, size_t size __attribute__((unused))) { size_t filtered; From 648f2d16c0486465ead0186ed9a1187c559d72dd Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Tue, 3 Sep 2024 20:58:49 +0300 Subject: [PATCH 174/402] configure: Add -mno-gpopt option for mips and mipsel targets Without it compiler generates GPREL16 references which do not work with our memory layout. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- configure.ac | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/configure.ac b/configure.ac index fe5493246..458b8382b 100644 --- a/configure.ac +++ b/configure.ac @@ -802,6 +802,17 @@ if test "x$target_cpu" = xmips || test "x$target_cpu" = xmipsel ; then if test "x$grub_cv_cc_mflush_func" = xyes; then TARGET_CFLAGS="$TARGET_CFLAGS -mflush-func=grub_red_herring" fi + + AC_CACHE_CHECK([whether -mno-gpopt works], [grub_cv_cc_mno_gpopt], [ + CFLAGS="$TARGET_CFLAGS -mno-gpopt -Werror" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_cc_mno_gpopt=yes], + [grub_cv_cc_mno_gpopt=no]) + ]) + + if test "x$grub_cv_cc_mno_gpopt" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -mno-gpopt" + fi fi From ed06516738d1b41285e43838d1fbd4f992f7faf3 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Tue, 3 Sep 2024 20:58:50 +0300 Subject: [PATCH 175/402] gentpl: Put boot/mips/startup_raw.S into beginning of the image Otherwise it breaks the decompressors for MIPS targets. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- gentpl.py | 4 ++-- grub-core/Makefile.core.def | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gentpl.py b/gentpl.py index 3b12eca6c..d8c6965d8 100644 --- a/gentpl.py +++ b/gentpl.py @@ -634,7 +634,7 @@ def extra_dist(defn): def extra_dep(defn): return foreach_value(defn, "depends", lambda value: value + " ") -def platform_sources(defn, p): return platform_values(defn, p, "") +def platform_sources(defn, p): return platform_values(defn, p, "_head") + platform_values(defn, p, "") def platform_nodist_sources(defn, p): return platform_values(defn, p, "_nodist") def platform_startup(defn, p): return platform_specific_values(defn, p, "_startup", "startup") @@ -660,7 +660,7 @@ def first_time(defn, snippet): def is_platform_independent(defn): if 'enable' in defn: return False - for suffix in [ "", "_nodist" ]: + for suffix in [ "", "_head", "_nodist" ]: template = platform_values(defn, GRUB_PLATFORMS[0], suffix) for platform in GRUB_PLATFORMS[1:]: if template != platform_values(defn, platform, suffix): diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 705d73fab..063ef5dd7 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -536,7 +536,7 @@ image = { image = { name = xz_decompress; - mips = boot/mips/startup_raw.S; + mips_head = boot/mips/startup_raw.S; common = boot/decompressor/minilib.c; common = boot/decompressor/xz.c; common = lib/xzembed/xz_dec_bcj.c; @@ -554,7 +554,7 @@ image = { image = { name = none_decompress; - mips = boot/mips/startup_raw.S; + mips_head = boot/mips/startup_raw.S; common = boot/decompressor/none.c; cppflags = '-DGRUB_EMBED_DECOMPRESSOR=1'; From 34b7f372126f0dd470732e4fb798b5a7fcaf1f75 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Tue, 3 Sep 2024 20:58:51 +0300 Subject: [PATCH 176/402] include/grub/offsets.h: Set mod_align to 4 on MIPS Module structure has natural alignment of 4. Respect it explicitly rather than relying on the fact that _end is usually aligned. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- include/grub/offsets.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/grub/offsets.h b/include/grub/offsets.h index 871e1cd4c..442dc31de 100644 --- a/include/grub/offsets.h +++ b/include/grub/offsets.h @@ -118,9 +118,9 @@ #define GRUB_KERNEL_SPARC64_IEEE1275_LOG_MOD_ALIGN 3 #define GRUB_KERNEL_SPARC64_IEEE1275_MOD_ALIGN (1 << GRUB_KERNEL_SPARC64_IEEE1275_LOG_MOD_ALIGN) -#define GRUB_KERNEL_MIPS_LOONGSON_MOD_ALIGN 0x1 -#define GRUB_KERNEL_MIPS_ARC_MOD_ALIGN 0x1 -#define GRUB_KERNEL_MIPS_QEMU_MIPS_MOD_ALIGN 0x1 +#define GRUB_KERNEL_MIPS_LOONGSON_MOD_ALIGN 0x4 +#define GRUB_KERNEL_MIPS_ARC_MOD_ALIGN 0x4 +#define GRUB_KERNEL_MIPS_QEMU_MIPS_MOD_ALIGN 0x4 #define GRUB_KERNEL_ARM_UBOOT_MOD_ALIGN 0x8 #define GRUB_KERNEL_ARM_UBOOT_TOTAL_MODULE_SIZE 0x4 From d333e8bb379fc1bbf2db0bfe97e9a5536439eb36 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Tue, 3 Sep 2024 20:58:52 +0300 Subject: [PATCH 177/402] util/grub-mkimagexx: Explicitly move modules to __bss_start for MIPS targets Assembly code looks for modules at __bss_start. Make this position explicit rather than matching BSS alignment and module alignment. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- util/grub-mkimagexx.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c index e50b29533..520c4f97c 100644 --- a/util/grub-mkimagexx.c +++ b/util/grub-mkimagexx.c @@ -2431,6 +2431,8 @@ SUFFIX (grub_mkimage_load_image) (const char *kernel_path, if (!is_relocatable (image_target)) { Elf_Addr current_address = layout->kernel_size; + Elf_Addr bss_start = layout->kernel_size; + bool is_first = true; for (i = 0, s = smd.sections; i < smd.num_sections; @@ -2453,6 +2455,12 @@ SUFFIX (grub_mkimage_load_image) (const char *kernel_path, current_address = grub_host_to_target_addr (s->sh_addr) - image_target->link_addr; + if (is_first == true) + { + bss_start = current_address; + is_first = false; + } + smd.vaddrs[i] = current_address + image_target->vaddr_offset; current_address += grub_host_to_target_addr (s->sh_size); @@ -2460,6 +2468,16 @@ SUFFIX (grub_mkimage_load_image) (const char *kernel_path, current_address = ALIGN_UP (current_address + image_target->vaddr_offset, image_target->section_align) - image_target->vaddr_offset; + + if (image_target->id == IMAGE_YEELOONG_FLASH + || image_target->id == IMAGE_FULOONG2F_FLASH + || image_target->id == IMAGE_LOONGSON_ELF + || image_target->id == IMAGE_QEMU_MIPS_FLASH + || image_target->id == IMAGE_MIPS_ARC) + { + layout->kernel_size = bss_start; + } + layout->bss_size = current_address - layout->kernel_size; } else From 95145eea5e4237539a99f279c6099c6288aa4079 Mon Sep 17 00:00:00 2001 From: Frediano Ziglio Date: Tue, 3 Sep 2024 09:00:27 +0100 Subject: [PATCH 178/402] loader/efi/linux: Update comment The function called is grub_utf8_to_utf16(). Signed-off-by: Frediano Ziglio Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/loader/efi/linux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c index bfbd95aee..5c1f91d97 100644 --- a/grub-core/loader/efi/linux.c +++ b/grub-core/loader/efi/linux.c @@ -216,7 +216,7 @@ grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args) grub_dprintf ("linux", "linux command line: '%s'\n", args); - /* Convert command line to UCS-2 */ + /* Convert command line to UTF-16. */ loaded_image = grub_efi_get_loaded_image (image_handle); if (loaded_image == NULL) { From 77cd623dee02624a193018062c85d87e80891c31 Mon Sep 17 00:00:00 2001 From: Frediano Ziglio Date: Tue, 3 Sep 2024 09:00:29 +0100 Subject: [PATCH 179/402] lib/x86_64/relocator_asm: Fix comment in code The instruction uses a 64-bit immediate. Signed-off-by: Frediano Ziglio Reviewed-by: Daniel Kiper --- grub-core/lib/x86_64/relocator_asm.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/lib/x86_64/relocator_asm.S b/grub-core/lib/x86_64/relocator_asm.S index 2ab6d8cb7..fd9b2b44e 100644 --- a/grub-core/lib/x86_64/relocator_asm.S +++ b/grub-core/lib/x86_64/relocator_asm.S @@ -22,7 +22,7 @@ .p2align 2 VARIABLE(grub_relocator_backward_start) - /* mov imm32, %rax */ + /* mov imm64, %rax */ .byte 0x48 .byte 0xb8 VARIABLE(grub_relocator_backward_dest) From 33cb8aecdd89f8fbd347a9275f02e9a539be0261 Mon Sep 17 00:00:00 2001 From: Frediano Ziglio Date: Tue, 3 Sep 2024 09:00:30 +0100 Subject: [PATCH 180/402] lib/x86_64/relocator_asm: Use .quad instead of .long They are single 64-bit values. Used in other assembly files too. Signed-off-by: Frediano Ziglio Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/lib/x86_64/relocator_asm.S | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/grub-core/lib/x86_64/relocator_asm.S b/grub-core/lib/x86_64/relocator_asm.S index fd9b2b44e..12728d8e1 100644 --- a/grub-core/lib/x86_64/relocator_asm.S +++ b/grub-core/lib/x86_64/relocator_asm.S @@ -26,21 +26,21 @@ VARIABLE(grub_relocator_backward_start) .byte 0x48 .byte 0xb8 VARIABLE(grub_relocator_backward_dest) - .long 0, 0 + .quad 0 movq %rax, %rdi /* mov imm64, %rax */ .byte 0x48 .byte 0xb8 VARIABLE(grub_relocator_backward_src) - .long 0, 0 + .quad 0 movq %rax, %rsi /* mov imm64, %rcx */ .byte 0x48 .byte 0xb9 VARIABLE(grub_relocator_backward_chunk_size) - .long 0, 0 + .quad 0 add %rcx, %rsi add %rcx, %rdi @@ -62,21 +62,21 @@ VARIABLE(grub_relocator_forward_start) .byte 0x48 .byte 0xb8 VARIABLE(grub_relocator_forward_dest) - .long 0, 0 + .quad 0 movq %rax, %rdi /* mov imm64, %rax */ .byte 0x48 .byte 0xb8 VARIABLE(grub_relocator_forward_src) - .long 0, 0 + .quad 0 movq %rax, %rsi /* mov imm64, %rcx */ .byte 0x48 .byte 0xb9 VARIABLE(grub_relocator_forward_chunk_size) - .long 0, 0 + .quad 0 /* Forward copy. */ cld From 92bed41bf81381038f5ae431884d33aa1d771bcd Mon Sep 17 00:00:00 2001 From: Frediano Ziglio Date: Tue, 3 Sep 2024 16:15:46 +0100 Subject: [PATCH 181/402] loader/efi/linux: Reuse len variable Signed-off-by: Frediano Ziglio Reviewed-by: Daniel Kiper --- grub-core/loader/efi/linux.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c index 5c1f91d97..64ff984f5 100644 --- a/grub-core/loader/efi/linux.c +++ b/grub-core/loader/efi/linux.c @@ -226,7 +226,7 @@ grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args) loaded_image->load_options_size = len = (grub_strlen (args) + 1) * sizeof (grub_efi_char16_t); loaded_image->load_options = - grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size)); + grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (len)); if (!loaded_image->load_options) return grub_errno; @@ -240,7 +240,7 @@ grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args) /* When successful, not reached */ grub_error (GRUB_ERR_BAD_OS, "start_image() returned 0x%" PRIxGRUB_EFI_UINTN_T, status); grub_efi_free_pages ((grub_addr_t) loaded_image->load_options, - GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size)); + GRUB_EFI_BYTES_TO_PAGES (len)); unload: b->unload_image (image_handle); From 9c34d56c2dafcd2737db0e3e49df63bce4d8b504 Mon Sep 17 00:00:00 2001 From: Frediano Ziglio Date: Tue, 3 Sep 2024 16:15:47 +0100 Subject: [PATCH 182/402] loader/efi/linux: Reset freed pointer Avoid dangling pointer. Code should not be reached but better safe than sorry. Signed-off-by: Frediano Ziglio Reviewed-by: Daniel Kiper --- grub-core/loader/efi/linux.c | 1 + 1 file changed, 1 insertion(+) diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c index 64ff984f5..78ea07ca8 100644 --- a/grub-core/loader/efi/linux.c +++ b/grub-core/loader/efi/linux.c @@ -241,6 +241,7 @@ grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args) grub_error (GRUB_ERR_BAD_OS, "start_image() returned 0x%" PRIxGRUB_EFI_UINTN_T, status); grub_efi_free_pages ((grub_addr_t) loaded_image->load_options, GRUB_EFI_BYTES_TO_PAGES (len)); + loaded_image->load_options = NULL; unload: b->unload_image (image_handle); From c5ae124e11f28f637cbd38cb4d6c1b9817baa135 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Mon, 12 Aug 2024 16:13:18 +0200 Subject: [PATCH 183/402] kern/riscv/efi/init: Use time register in grub_efi_get_time_ms() The cycle register is not guaranteed to count at constant frequency. If it is counting at all depends on the state the performance monitoring unit. Use the time register to measure time. Signed-off-by: Heinrich Schuchardt Reviewed-by: Daniel Kiper --- grub-core/kern/riscv/efi/init.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/grub-core/kern/riscv/efi/init.c b/grub-core/kern/riscv/efi/init.c index 38795fe67..0d7de4f54 100644 --- a/grub-core/kern/riscv/efi/init.c +++ b/grub-core/kern/riscv/efi/init.c @@ -33,16 +33,15 @@ grub_efi_get_time_ms (void) grub_uint64_t tmr; #if __riscv_xlen == 64 - asm volatile ("rdcycle %0" : "=r" (tmr)); + asm volatile ("rdtime %0" : "=r"(tmr)); #else grub_uint32_t lo, hi, tmp; - asm volatile ( - "1:\n" - "rdcycleh %0\n" - "rdcycle %1\n" - "rdcycleh %2\n" - "bne %0, %2, 1b" - : "=&r" (hi), "=&r" (lo), "=&r" (tmp)); + asm volatile ("1:\n" + "rdtimeh %0\n" + "rdtime %1\n" + "rdtimeh %2\n" + "bne %0, %2, 1b" + : "=&r" (hi), "=&r" (lo), "=&r" (tmp)); tmr = ((grub_uint64_t)hi << 32) | lo; #endif From e5f047be053a1d6d10590bc413d089535a13454c Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Thu, 3 Oct 2024 15:23:08 +0800 Subject: [PATCH 184/402] efi/console: Properly clear leftover artifacts from the screen A regression in GRUB 2.12 causes the GRUB screen to become cluttered with artifacts from the previous screen whether it's the UEFI post UI, UEFI shell or any graphical UI running before GRUB. This issue occurs in situations like booting GRUB from the UEFI shell and going straight to the rescue or command shell causing visual discomfort. The regression was introduced by commit 2d7c3abd8 (efi/console: Do not set text-mode until it is actually needed). To address the screen flickering issue this commit suppresses the text-mode setting until the first output is requested. Before text-mode is set any attempt to clear the screen has no effect. This inactive period renders the clear screen ineffective in early boot stages, potentially leaving leftover artifacts that will clutter the GRUB console display, as there is no guarantee there will always be a clear screen after the first output. The issue is fixed by ensuring grub_console_cls() to work through lazy mode-setting, while also avoiding screen clearing for the hidden menu which the flicker-free patch aims to improve. Fixes: 2d7c3abd8 (efi/console: Do not set text-mode until we actually need it) Signed-off-by: Michael Chang Reviewed-by: Daniel Kiper --- grub-core/normal/menu.c | 7 ++++--- grub-core/term/efi/console.c | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c index 6a90e091f..f24544b27 100644 --- a/grub-core/normal/menu.c +++ b/grub-core/normal/menu.c @@ -881,13 +881,14 @@ show_menu (grub_menu_t menu, int nested, int autobooted) if (! e) continue; /* Menu is empty. */ - grub_cls (); - if (auto_boot) grub_menu_execute_with_fallback (menu, e, autobooted, &execution_callback, ¬ify_boot); else - grub_menu_execute_entry (e, 0); + { + grub_cls (); + grub_menu_execute_entry (e, 0); + } if (autobooted) break; } diff --git a/grub-core/term/efi/console.c b/grub-core/term/efi/console.c index bb587f39d..258b52737 100644 --- a/grub-core/term/efi/console.c +++ b/grub-core/term/efi/console.c @@ -432,7 +432,7 @@ grub_console_cls (struct grub_term_output *term __attribute__ ((unused))) grub_efi_simple_text_output_interface_t *o; grub_efi_int32_t orig_attr; - if (grub_efi_is_finished || text_mode != GRUB_TEXT_MODE_AVAILABLE) + if (grub_prepare_for_text_output (term) != GRUB_ERR_NONE) return; o = grub_efi_system_table->con_out; From 3808b1a9bd47cf783d4b8775fbc788a9ce5afdd4 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Thu, 3 Oct 2024 15:23:15 +0800 Subject: [PATCH 185/402] net/drivers/efi/efinet: Skip virtual VLAN devices during card enumeration Similarly to the issue described in commit c52ae4057 (efinet: skip virtual IPv4 and IPv6 devices during card enumeration) the UEFI PXE driver creates additional VLAN child devices when a VLAN ID is configured on a network interface associated with a physical NIC. These virtual VLAN devices must be skipped during card enumeration to ensure that the subsequent SNP exclusive open operation targets the correct physical card instances. Otherwise packet transfer would fail. A device path example with VLAN nodes: /MAC(123456789ABC,0x1)/Vlan(20)/IPv4(0.0.0.0,0x0,DHCP,0.0.0.0,0.0.0.0,0.0.0.0) Signed-off-by: Michael Chang Reviewed-by: Daniel Kiper --- grub-core/net/drivers/efi/efinet.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c index 3ea25cf98..58fe381ab 100644 --- a/grub-core/net/drivers/efi/efinet.c +++ b/grub-core/net/drivers/efi/efinet.c @@ -276,7 +276,8 @@ grub_efinet_findcards (void) || GRUB_EFI_DEVICE_PATH_SUBTYPE (child) == GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE) && parent && GRUB_EFI_DEVICE_PATH_TYPE (parent) == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE - && GRUB_EFI_DEVICE_PATH_SUBTYPE (parent) == GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE) + && (GRUB_EFI_DEVICE_PATH_SUBTYPE (parent) == GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE + || GRUB_EFI_DEVICE_PATH_SUBTYPE (parent) == GRUB_EFI_VLAN_DEVICE_PATH_SUBTYPE)) continue; net = grub_efi_open_protocol (*handle, &net_io_guid, @@ -389,6 +390,15 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; dup_ldp->length = sizeof (*dup_ldp); + + dup_ldp = grub_efi_find_last_device_path (dup_dp); + if (GRUB_EFI_DEVICE_PATH_SUBTYPE (dup_ldp) == GRUB_EFI_VLAN_DEVICE_PATH_SUBTYPE) + { + dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + dup_ldp->length = sizeof (*dup_ldp); + } + match = grub_efi_compare_device_paths (dup_dp, cdp) == 0; grub_free (dup_dp); if (!match) From 86ec48882bd0b06268f93033bce9eea168188fae Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Thu, 3 Oct 2024 15:23:22 +0800 Subject: [PATCH 186/402] commands/tpm: Skip loopback image measurement The loopback image is configured to function as a disk by being mapped as a block device. Instead of measuring the entire block device we should focus on tracking the individual files accessed from it. For example, we do not directly measure block devices like hd0 disk but the files opened from it. This method is important to avoid running out of memory since loopback images can be very large. Trying to read and measure the whole image at once could cause out of memory errors and disrupt the boot process. Signed-off-by: Michael Chang Reviewed-by: Daniel Kiper --- grub-core/commands/tpm.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/grub-core/commands/tpm.c b/grub-core/commands/tpm.c index 324423ef8..dde74ab83 100644 --- a/grub-core/commands/tpm.c +++ b/grub-core/commands/tpm.c @@ -36,6 +36,16 @@ grub_tpm_verify_init (grub_file_t io, { *context = io->name; *flags |= GRUB_VERIFY_FLAGS_SINGLE_CHUNK; + + /* + * The loopback image is mapped as a disk allowing it to function like + * a block device. However, we measure files read from the block device + * not the device itself. For example, we don't measure block devices like + * hd0 disk directly. This process is crucial to prevent out-of-memory + * errors as loopback images are inherently large. + */ + if ((type & GRUB_FILE_TYPE_MASK) == GRUB_FILE_TYPE_LOOPBACK) + *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; return GRUB_ERR_NONE; } From d96cfd7bf86e177260557efb3c31ecbc9df34234 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Sun, 6 Oct 2024 17:14:42 +0300 Subject: [PATCH 187/402] i386/msr: Merge rdmsr.h and wrmsr.h into msr.h It does not make sense to have separate headers for individual static functions. So, make one common place to store them. Signed-off-by: Daniel Kiper Signed-off-by: Sergii Dmytruk Reviewed-by: Ross Philipson Reviewed-by: Daniel Kiper --- grub-core/commands/i386/rdmsr.c | 2 +- grub-core/commands/i386/wrmsr.c | 2 +- include/grub/i386/{wrmsr.h => msr.h} | 16 +++++++++--- include/grub/i386/rdmsr.h | 37 ---------------------------- 4 files changed, 15 insertions(+), 42 deletions(-) rename include/grub/i386/{wrmsr.h => msr.h} (78%) delete mode 100644 include/grub/i386/rdmsr.h diff --git a/grub-core/commands/i386/rdmsr.c b/grub-core/commands/i386/rdmsr.c index 46c4346da..fa4622f9e 100644 --- a/grub-core/commands/i386/rdmsr.c +++ b/grub-core/commands/i386/rdmsr.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include GRUB_MOD_LICENSE("GPLv3+"); diff --git a/grub-core/commands/i386/wrmsr.c b/grub-core/commands/i386/wrmsr.c index 1b143b888..8f352f205 100644 --- a/grub-core/commands/i386/wrmsr.c +++ b/grub-core/commands/i386/wrmsr.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include GRUB_MOD_LICENSE("GPLv3+"); diff --git a/include/grub/i386/wrmsr.h b/include/grub/i386/msr.h similarity index 78% rename from include/grub/i386/wrmsr.h rename to include/grub/i386/msr.h index dea60aed1..7b52b5d61 100644 --- a/include/grub/i386/wrmsr.h +++ b/include/grub/i386/msr.h @@ -16,14 +16,24 @@ * along with GRUB. If not, see . */ -#ifndef GRUB_WRMSR_H -#define GRUB_WRMSR_H 1 +#ifndef GRUB_I386_MSR_H +#define GRUB_I386_MSR_H 1 /* * TODO: Add a general protection exception handler. * Accessing a reserved or unimplemented MSR address results in a GP#. */ +static inline grub_uint64_t +grub_msr_read (grub_uint32_t msr_id) +{ + grub_uint32_t low, high; + + asm volatile ("rdmsr" : "=a" (low), "=d" (high) : "c" (msr_id)); + + return ((grub_uint64_t) high << 32) | low; +} + static inline void grub_msr_write(grub_uint32_t msr_id, grub_uint64_t msr_value) { @@ -32,4 +42,4 @@ grub_msr_write(grub_uint32_t msr_id, grub_uint64_t msr_value) asm volatile ("wrmsr" : : "c" (msr_id), "a" (low), "d" (high)); } -#endif /* GRUB_WRMSR_H */ +#endif /* GRUB_I386_MSR_H */ diff --git a/include/grub/i386/rdmsr.h b/include/grub/i386/rdmsr.h deleted file mode 100644 index c0a0c717a..000000000 --- a/include/grub/i386/rdmsr.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2019 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 GRUB_RDMSR_H -#define GRUB_RDMSR_H 1 - -/* - * TODO: Add a general protection exception handler. - * Accessing a reserved or unimplemented MSR address results in a GP#. - */ - -static inline grub_uint64_t -grub_msr_read (grub_uint32_t msr_id) -{ - grub_uint32_t low, high; - - asm volatile ("rdmsr" : "=a" (low), "=d" (high) : "c" (msr_id)); - - return ((grub_uint64_t)high << 32) | low; -} - -#endif /* GRUB_RDMSR_H */ From 929fafdf5ec3bcbcb47337b187d7f2d48229e086 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Sun, 6 Oct 2024 17:14:43 +0300 Subject: [PATCH 188/402] i386/msr: Rename grub_msr_read() and grub_msr_write() Use more obvious names which match corresponding instructions: * grub_msr_read() => grub_rdmsr(), * grub_msr_write() => grub_wrmsr(). Signed-off-by: Daniel Kiper Signed-off-by: Sergii Dmytruk Reviewed-by: Ross Philipson Reviewed-by: Daniel Kiper --- grub-core/commands/i386/rdmsr.c | 2 +- grub-core/commands/i386/wrmsr.c | 2 +- include/grub/i386/msr.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/grub-core/commands/i386/rdmsr.c b/grub-core/commands/i386/rdmsr.c index fa4622f9e..89ece7657 100644 --- a/grub-core/commands/i386/rdmsr.c +++ b/grub-core/commands/i386/rdmsr.c @@ -76,7 +76,7 @@ grub_cmd_msr_read (grub_extcmd_context_t ctxt, int argc, char **argv) if (*ptr != '\0') return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid argument")); - value = grub_msr_read (addr); + value = grub_rdmsr (addr); if (ctxt->state[0].set) { diff --git a/grub-core/commands/i386/wrmsr.c b/grub-core/commands/i386/wrmsr.c index 8f352f205..cf6bf6c8f 100644 --- a/grub-core/commands/i386/wrmsr.c +++ b/grub-core/commands/i386/wrmsr.c @@ -77,7 +77,7 @@ grub_cmd_msr_write (grub_command_t cmd __attribute__ ((unused)), int argc, char if (*ptr != '\0') return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid argument")); - grub_msr_write (addr, value); + grub_wrmsr (addr, value); return GRUB_ERR_NONE; } diff --git a/include/grub/i386/msr.h b/include/grub/i386/msr.h index 7b52b5d61..4fba1b8e0 100644 --- a/include/grub/i386/msr.h +++ b/include/grub/i386/msr.h @@ -25,7 +25,7 @@ */ static inline grub_uint64_t -grub_msr_read (grub_uint32_t msr_id) +grub_rdmsr (grub_uint32_t msr_id) { grub_uint32_t low, high; @@ -35,7 +35,7 @@ grub_msr_read (grub_uint32_t msr_id) } static inline void -grub_msr_write(grub_uint32_t msr_id, grub_uint64_t msr_value) +grub_wrmsr (grub_uint32_t msr_id, grub_uint64_t msr_value) { grub_uint32_t low = msr_value, high = msr_value >> 32; From 1b1061409d8945868afac00d9308e9dd3289033d Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Sun, 6 Oct 2024 17:14:44 +0300 Subject: [PATCH 189/402] i386/msr: Extract and improve MSR support detection code Currently rdmsr and wrmsr commands have own MSR support detection code. This code is the same. So, it is duplicated. Additionally, this code cannot be reused by others. Hence, extract this code to a function and make it public. By the way, improve a code a bit. Additionally, use GRUB_ERR_BAD_DEVICE instead of GRUB_ERR_BUG to signal an error because errors encountered by this new routine are not bugs. Signed-off-by: Daniel Kiper Signed-off-by: Sergii Dmytruk Reviewed-by: Ross Philipson Reviewed-by: Daniel Kiper --- grub-core/commands/i386/rdmsr.c | 21 +++++---------------- grub-core/commands/i386/wrmsr.c | 21 +++++---------------- include/grub/i386/msr.h | 29 +++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 32 deletions(-) diff --git a/grub-core/commands/i386/rdmsr.c b/grub-core/commands/i386/rdmsr.c index 89ece7657..2e42f6197 100644 --- a/grub-core/commands/i386/rdmsr.c +++ b/grub-core/commands/i386/rdmsr.c @@ -42,27 +42,16 @@ static const struct grub_arg_option options[] = static grub_err_t grub_cmd_msr_read (grub_extcmd_context_t ctxt, int argc, char **argv) { - grub_uint32_t manufacturer[3], max_cpuid, a, b, c, features, addr; + grub_err_t err; + grub_uint32_t addr; grub_uint64_t value; const char *ptr; char buf[sizeof("1122334455667788")]; - /* - * The CPUID instruction should be used to determine whether MSRs - * are supported. (CPUID.01H:EDX[5] = 1) - */ - if (! grub_cpu_is_cpuid_supported ()) - return grub_error (GRUB_ERR_BUG, N_("unsupported instruction")); + err = grub_cpu_is_msr_supported (); - grub_cpuid (0, max_cpuid, manufacturer[0], manufacturer[2], manufacturer[1]); - - if (max_cpuid < 1) - return grub_error (GRUB_ERR_BUG, N_("unsupported instruction")); - - grub_cpuid (1, a, b, c, features); - - if (!(features & (1 << 5))) - return grub_error (GRUB_ERR_BUG, N_("unsupported instruction")); + if (err != GRUB_ERR_NONE) + return grub_error (err, N_("RDMSR is unsupported")); if (argc != 1) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); diff --git a/grub-core/commands/i386/wrmsr.c b/grub-core/commands/i386/wrmsr.c index cf6bf6c8f..7fbedaed9 100644 --- a/grub-core/commands/i386/wrmsr.c +++ b/grub-core/commands/i386/wrmsr.c @@ -36,26 +36,15 @@ static grub_command_t cmd_write; static grub_err_t grub_cmd_msr_write (grub_command_t cmd __attribute__ ((unused)), int argc, char **argv) { - grub_uint32_t manufacturer[3], max_cpuid, a, b, c, features, addr; + grub_err_t err; + grub_uint32_t addr; grub_uint64_t value; const char *ptr; - /* - * The CPUID instruction should be used to determine whether MSRs - * are supported. (CPUID.01H:EDX[5] = 1) - */ - if (!grub_cpu_is_cpuid_supported ()) - return grub_error (GRUB_ERR_BUG, N_("unsupported instruction")); + err = grub_cpu_is_msr_supported (); - grub_cpuid (0, max_cpuid, manufacturer[0], manufacturer[2], manufacturer[1]); - - if (max_cpuid < 1) - return grub_error (GRUB_ERR_BUG, N_("unsupported instruction")); - - grub_cpuid (1, a, b, c, features); - - if (!(features & (1 << 5))) - return grub_error (GRUB_ERR_BUG, N_("unsupported instruction")); + if (err != GRUB_ERR_NONE) + return grub_error (err, N_("WRMSR is unsupported")); if (argc != 2) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected")); diff --git a/include/grub/i386/msr.h b/include/grub/i386/msr.h index 4fba1b8e0..1e838c022 100644 --- a/include/grub/i386/msr.h +++ b/include/grub/i386/msr.h @@ -19,6 +19,35 @@ #ifndef GRUB_I386_MSR_H #define GRUB_I386_MSR_H 1 +#include +#include +#include + +static inline grub_err_t +grub_cpu_is_msr_supported (void) +{ + grub_uint32_t eax, ebx, ecx, edx; + + /* + * The CPUID instruction should be used to determine whether MSRs + * are supported, CPUID.01H:EDX[5] = 1. + */ + if (!grub_cpu_is_cpuid_supported ()) + return GRUB_ERR_BAD_DEVICE; + + grub_cpuid (0, eax, ebx, ecx, edx); + + if (eax < 1) + return GRUB_ERR_BAD_DEVICE; + + grub_cpuid (1, eax, ebx, ecx, edx); + + if (!(edx & (1 << 5))) + return GRUB_ERR_BAD_DEVICE; + + return GRUB_ERR_NONE; +} + /* * TODO: Add a general protection exception handler. * Accessing a reserved or unimplemented MSR address results in a GP#. From 95a7bfef5d94a70cf5a54c4e7036b6810869ef48 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Sun, 6 Oct 2024 17:14:45 +0300 Subject: [PATCH 190/402] i386/memory: Rename PAGE_SHIFT to GRUB_PAGE_SHIFT This fixes naming inconsistency that goes against coding style as well as helps to avoid potential conflicts and confusion as this constant is used in multiple places. Signed-off-by: Daniel Kiper Signed-off-by: Sergii Dmytruk Reviewed-by: Ross Philipson Reviewed-by: Daniel Kiper --- grub-core/lib/i386/xen/relocator.S | 6 +++--- grub-core/lib/x86_64/xen/relocator.S | 4 ++-- grub-core/loader/i386/xen.c | 28 ++++++++++++++-------------- include/grub/i386/memory.h | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/grub-core/lib/i386/xen/relocator.S b/grub-core/lib/i386/xen/relocator.S index 96e51b59a..dab4d8ace 100644 --- a/grub-core/lib/i386/xen/relocator.S +++ b/grub-core/lib/i386/xen/relocator.S @@ -75,10 +75,10 @@ VARIABLE(grub_relocator_xen_mfn_list) .long 0 movl 0(%eax, %ebp, 4), %ecx /* mfn */ movl %ebp, %ebx - shll $PAGE_SHIFT, %ebx /* virtual address (1:1 mapping) */ + shll $GRUB_PAGE_SHIFT, %ebx /* virtual address (1:1 mapping) */ movl %ecx, %edx - shll $PAGE_SHIFT, %ecx /* prepare pte low part */ - shrl $(32 - PAGE_SHIFT), %edx /* pte high part */ + shll $GRUB_PAGE_SHIFT, %ecx /* prepare pte low part */ + shrl $(32 - GRUB_PAGE_SHIFT), %edx /* pte high part */ orl $(GRUB_PAGE_PRESENT | GRUB_PAGE_USER), %ecx /* pte low */ movl $UVMF_INVLPG, %esi movl $__HYPERVISOR_update_va_mapping, %eax diff --git a/grub-core/lib/x86_64/xen/relocator.S b/grub-core/lib/x86_64/xen/relocator.S index f5364ed0f..852cd40aa 100644 --- a/grub-core/lib/x86_64/xen/relocator.S +++ b/grub-core/lib/x86_64/xen/relocator.S @@ -60,9 +60,9 @@ LOCAL(cont): jz 3f 2: movq %r12, %rdi - shlq $PAGE_SHIFT, %rdi /* virtual address (1:1 mapping) */ + shlq $GRUB_PAGE_SHIFT, %rdi /* virtual address (1:1 mapping) */ movq (%rbx, %r12, 8), %rsi /* mfn */ - shlq $PAGE_SHIFT, %rsi + shlq $GRUB_PAGE_SHIFT, %rsi orq $(GRUB_PAGE_PRESENT | GRUB_PAGE_USER), %rsi /* Build pte */ movq $UVMF_INVLPG, %rdx movq %rcx, %r9 /* %rcx clobbered by hypercall */ diff --git a/grub-core/loader/i386/xen.c b/grub-core/loader/i386/xen.c index 3b856e842..520367732 100644 --- a/grub-core/loader/i386/xen.c +++ b/grub-core/loader/i386/xen.c @@ -92,7 +92,7 @@ static struct xen_loader_state xen_state; static grub_dl_t my_mod; -#define PAGE_SIZE (1UL << PAGE_SHIFT) +#define PAGE_SIZE (1UL << GRUB_PAGE_SHIFT) #define MAX_MODULES (PAGE_SIZE / sizeof (struct xen_multiboot_mod_list)) #define STACK_SIZE 1048576 #define ADDITIONAL_SIZE (1 << 19) @@ -103,7 +103,7 @@ static grub_dl_t my_mod; static grub_uint64_t page2offset (grub_uint64_t page) { - return page << PAGE_SHIFT; + return page << GRUB_PAGE_SHIFT; } static grub_err_t @@ -142,7 +142,7 @@ get_pgtable_size (grub_uint64_t from, grub_uint64_t to, grub_uint64_t pfn) continue; } - bits = PAGE_SHIFT + (i + 1) * LOG_POINTERS_PER_PAGE; + bits = GRUB_PAGE_SHIFT + (i + 1) * LOG_POINTERS_PER_PAGE; mask = (1ULL << bits) - 1; map->lvls[i].virt_start = map->area.virt_start & ~mask; map->lvls[i].virt_end = map->area.virt_end | mask; @@ -247,11 +247,11 @@ generate_page_table (grub_xen_mfn_t *mfn_list) if (lvl->virt_start >= end || lvl->virt_end <= start) continue; p_s = (grub_max (start, lvl->virt_start) - start) >> - (PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE); + (GRUB_PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE); p_e = (grub_min (end, lvl->virt_end) - start) >> - (PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE); + (GRUB_PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE); pfn = ((grub_max (start, lvl->virt_start) - lvl->virt_start) >> - (PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE)) + lvl->pfn_start; + (GRUB_PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE)) + lvl->pfn_start; grub_dprintf ("xen", "write page table entries level %d pg %p " "mapping %d/%d index %lx-%lx pfn %llx\n", l, pg, m1, m2, p_s, p_e, (unsigned long long) pfn); @@ -329,16 +329,16 @@ grub_xen_p2m_alloc (void) { err = get_pgtable_size (xen_state.xen_inf.p2m_base, xen_state.xen_inf.p2m_base + p2msize, - (xen_state.max_addr + p2msize) >> PAGE_SHIFT); + (xen_state.max_addr + p2msize) >> GRUB_PAGE_SHIFT); if (err) return err; - map->area.pfn_start = xen_state.max_addr >> PAGE_SHIFT; + map->area.pfn_start = xen_state.max_addr >> GRUB_PAGE_SHIFT; p2malloc = p2msize + page2offset (map->area.n_pt_pages); xen_state.n_mappings++; xen_state.next_start.mfn_list = xen_state.xen_inf.p2m_base; xen_state.next_start.first_p2m_pfn = map->area.pfn_start; - xen_state.next_start.nr_p2m_frames = p2malloc >> PAGE_SHIFT; + xen_state.next_start.nr_p2m_frames = p2malloc >> GRUB_PAGE_SHIFT; } else { @@ -381,7 +381,7 @@ grub_xen_special_alloc (void) xen_state.virt_start_info = get_virtual_current_address (ch); xen_state.max_addr = ALIGN_UP (xen_state.max_addr + sizeof (xen_state.next_start), PAGE_SIZE); - xen_state.console_pfn = xen_state.max_addr >> PAGE_SHIFT; + xen_state.console_pfn = xen_state.max_addr >> GRUB_PAGE_SHIFT; xen_state.max_addr += 2 * PAGE_SIZE; xen_state.next_start.nr_pages = grub_xen_start_page_addr->nr_pages; @@ -413,7 +413,7 @@ grub_xen_pt_alloc (void) xen_state.next_start.pt_base = xen_state.max_addr + xen_state.xen_inf.virt_base; - nr_info_pages = xen_state.max_addr >> PAGE_SHIFT; + nr_info_pages = xen_state.max_addr >> GRUB_PAGE_SHIFT; nr_need_pages = nr_info_pages; while (1) @@ -461,7 +461,7 @@ grub_xen_pt_alloc (void) xen_state.max_addr + STACK_SIZE + xen_state.xen_inf.virt_base; xen_state.next_start.nr_pt_frames = nr_need_pages; xen_state.max_addr = try_virt_end - xen_state.xen_inf.virt_base; - xen_state.pgtbl_end = xen_state.max_addr >> PAGE_SHIFT; + xen_state.pgtbl_end = xen_state.max_addr >> GRUB_PAGE_SHIFT; xen_state.map_reloc->where = (grub_uint64_t *) ((char *) map->where + page2offset (map->area.n_pt_pages)); @@ -515,7 +515,7 @@ grub_xen_boot (void) if (err) return err; - nr_pages = xen_state.max_addr >> PAGE_SHIFT; + nr_pages = xen_state.max_addr >> GRUB_PAGE_SHIFT; grub_dprintf ("xen", "bootstrap domain %llx+%llx\n", (unsigned long long) xen_state.xen_inf.virt_base, @@ -818,7 +818,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), if (xen_state.xen_inf.unmapped_initrd) { xen_state.next_start.flags |= SIF_MOD_START_PFN; - xen_state.next_start.mod_start = xen_state.max_addr >> PAGE_SHIFT; + xen_state.next_start.mod_start = xen_state.max_addr >> GRUB_PAGE_SHIFT; } else xen_state.next_start.mod_start = diff --git a/include/grub/i386/memory.h b/include/grub/i386/memory.h index 5cb607fb4..7be57d6d7 100644 --- a/include/grub/i386/memory.h +++ b/include/grub/i386/memory.h @@ -20,7 +20,7 @@ #ifndef GRUB_MEMORY_CPU_HEADER #define GRUB_MEMORY_CPU_HEADER 1 -#define PAGE_SHIFT 12 +#define GRUB_PAGE_SHIFT 12 /* The flag for protected mode. */ #define GRUB_MEMORY_CPU_CR0_PE_ON 0x1 From 616adeb80ba9c26a9aad8f248dfb52f3e09bf818 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Sun, 6 Oct 2024 17:14:46 +0300 Subject: [PATCH 191/402] i386/memory: Rename PAGE_SIZE to GRUB_PAGE_SIZE and make it global This is an x86-specific thing and should be available globally. Signed-off-by: Daniel Kiper Signed-off-by: Sergii Dmytruk Reviewed-by: Ross Philipson Reviewed-by: Daniel Kiper --- grub-core/loader/i386/xen.c | 35 +++++++++++++++++------------------ include/grub/i386/memory.h | 1 + 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/grub-core/loader/i386/xen.c b/grub-core/loader/i386/xen.c index 520367732..dcdf005df 100644 --- a/grub-core/loader/i386/xen.c +++ b/grub-core/loader/i386/xen.c @@ -92,8 +92,7 @@ static struct xen_loader_state xen_state; static grub_dl_t my_mod; -#define PAGE_SIZE (1UL << GRUB_PAGE_SHIFT) -#define MAX_MODULES (PAGE_SIZE / sizeof (struct xen_multiboot_mod_list)) +#define MAX_MODULES (GRUB_PAGE_SIZE / sizeof (struct xen_multiboot_mod_list)) #define STACK_SIZE 1048576 #define ADDITIONAL_SIZE (1 << 19) #define ALIGN_SIZE (1 << 22) @@ -229,7 +228,7 @@ generate_page_table (grub_xen_mfn_t *mfn_list) for (m1 = 0; m1 < xen_state.n_mappings; m1++) grub_memset (xen_state.mappings[m1].where, 0, - xen_state.mappings[m1].area.n_pt_pages * PAGE_SIZE); + xen_state.mappings[m1].area.n_pt_pages * GRUB_PAGE_SIZE); for (l = NUMBER_OF_LEVELS - 1; l >= 0; l--) { @@ -324,7 +323,7 @@ grub_xen_p2m_alloc (void) map = xen_state.mappings + xen_state.n_mappings; p2msize = ALIGN_UP (sizeof (grub_xen_mfn_t) * - grub_xen_start_page_addr->nr_pages, PAGE_SIZE); + grub_xen_start_page_addr->nr_pages, GRUB_PAGE_SIZE); if (xen_state.xen_inf.has_p2m_base) { err = get_pgtable_size (xen_state.xen_inf.p2m_base, @@ -380,9 +379,9 @@ grub_xen_special_alloc (void) xen_state.state.start_info = xen_state.max_addr + xen_state.xen_inf.virt_base; xen_state.virt_start_info = get_virtual_current_address (ch); xen_state.max_addr = - ALIGN_UP (xen_state.max_addr + sizeof (xen_state.next_start), PAGE_SIZE); + ALIGN_UP (xen_state.max_addr + sizeof (xen_state.next_start), GRUB_PAGE_SIZE); xen_state.console_pfn = xen_state.max_addr >> GRUB_PAGE_SHIFT; - xen_state.max_addr += 2 * PAGE_SIZE; + xen_state.max_addr += 2 * GRUB_PAGE_SIZE; xen_state.next_start.nr_pages = grub_xen_start_page_addr->nr_pages; grub_memcpy (xen_state.next_start.magic, grub_xen_start_page_addr->magic, @@ -431,9 +430,9 @@ grub_xen_pt_alloc (void) /* Map the relocator page either at virtual 0 or after end of area. */ nr_need_pages = nr_info_pages + map->area.n_pt_pages; if (xen_state.xen_inf.virt_base) - err = get_pgtable_size (0, PAGE_SIZE, nr_need_pages); + err = get_pgtable_size (0, GRUB_PAGE_SIZE, nr_need_pages); else - err = get_pgtable_size (try_virt_end, try_virt_end + PAGE_SIZE, + err = get_pgtable_size (try_virt_end, try_virt_end + GRUB_PAGE_SIZE, nr_need_pages); if (err) return err; @@ -538,7 +537,7 @@ grub_xen_boot (void) return grub_relocator_xen_boot (xen_state.relocator, xen_state.state, nr_pages, xen_state.xen_inf.virt_base < - PAGE_SIZE ? page2offset (nr_pages) : 0, + GRUB_PAGE_SIZE ? page2offset (nr_pages) : 0, xen_state.pgtbl_end - 1, page2offset (xen_state.pgtbl_end - 1) + xen_state.xen_inf.virt_base); @@ -677,7 +676,7 @@ grub_cmd_xen (grub_command_t cmd __attribute__ ((unused)), goto fail; } - if (xen_state.xen_inf.virt_base & (PAGE_SIZE - 1)) + if (xen_state.xen_inf.virt_base & (GRUB_PAGE_SIZE - 1)) { grub_error (GRUB_ERR_BAD_OS, "unaligned virt_base"); goto fail; @@ -700,10 +699,10 @@ grub_cmd_xen (grub_command_t cmd __attribute__ ((unused)), kern_start = grub_min (kern_start, xen_state.xen_inf.hypercall_page - xen_state.xen_inf.virt_base); kern_end = grub_max (kern_end, xen_state.xen_inf.hypercall_page - - xen_state.xen_inf.virt_base + PAGE_SIZE); + xen_state.xen_inf.virt_base + GRUB_PAGE_SIZE); } - xen_state.max_addr = ALIGN_UP (kern_end, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (kern_end, GRUB_PAGE_SIZE); if (grub_sub (kern_end, kern_start, &sz)) @@ -730,7 +729,7 @@ grub_cmd_xen (grub_command_t cmd __attribute__ ((unused)), if (xen_state.xen_inf.has_hypercall_page) { unsigned i; - for (i = 0; i < PAGE_SIZE / HYPERCALL_INTERFACE_SIZE; i++) + for (i = 0; i < GRUB_PAGE_SIZE / HYPERCALL_INTERFACE_SIZE; i++) set_hypercall_interface ((grub_uint8_t *) kern_chunk_src + i * HYPERCALL_INTERFACE_SIZE + xen_state.xen_inf.hypercall_page - @@ -828,7 +827,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), (unsigned) (xen_state.max_addr + xen_state.xen_inf.virt_base), (unsigned) size); - xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, GRUB_PAGE_SIZE); fail: grub_initrd_close (&initrd_ctx); @@ -882,7 +881,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), { xen_state.xen_inf.unmapped_initrd = 0; xen_state.n_modules = 0; - xen_state.max_addr = ALIGN_UP (xen_state.max_addr, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (xen_state.max_addr, GRUB_PAGE_SIZE); xen_state.modules_target_start = xen_state.max_addr; xen_state.next_start.mod_start = xen_state.max_addr + xen_state.xen_inf.virt_base; @@ -902,7 +901,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), MAX_MODULES * sizeof (xen_state.module_info_page[0]); } - xen_state.max_addr = ALIGN_UP (xen_state.max_addr, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (xen_state.max_addr, GRUB_PAGE_SIZE); file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_INITRD | (nounzip ? GRUB_FILE_TYPE_NO_DECOMPRESS : GRUB_FILE_TYPE_NONE)); @@ -925,7 +924,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), xen_state.module_info_page[xen_state.n_modules].cmdline = xen_state.max_addr - xen_state.modules_target_start; - xen_state.max_addr = ALIGN_UP (xen_state.max_addr + cmdline_len, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (xen_state.max_addr + cmdline_len, GRUB_PAGE_SIZE); if (size) { @@ -952,7 +951,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), xen_state.n_modules++; grub_dprintf ("xen", "module, addr=0x%x, size=0x%x\n", (unsigned) xen_state.max_addr, (unsigned) size); - xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, PAGE_SIZE); + xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, GRUB_PAGE_SIZE); fail: diff --git a/include/grub/i386/memory.h b/include/grub/i386/memory.h index 7be57d6d7..c64529630 100644 --- a/include/grub/i386/memory.h +++ b/include/grub/i386/memory.h @@ -21,6 +21,7 @@ #define GRUB_MEMORY_CPU_HEADER 1 #define GRUB_PAGE_SHIFT 12 +#define GRUB_PAGE_SIZE (1UL << GRUB_PAGE_SHIFT) /* The flag for protected mode. */ #define GRUB_MEMORY_CPU_CR0_PE_ON 0x1 From 246c82cdae4be6121ad3cede2f2c8995aebf752f Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 9 Oct 2024 09:16:36 +0100 Subject: [PATCH 192/402] modules: Make .module_license read-only Currently .module_license is set writable, that is, the section has the SHF_WRITE flag set, in the module's ELF headers. This probably never actually matters but it can't possibly be correct. The patch sets that data as "const" which causes that flag not to be set. Signed-off-by: Peter Jones Signed-off-by: Jan Setje-Eilers Signed-off-by: Mate Kukri Reviewed-By: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- include/grub/dl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/grub/dl.h b/include/grub/dl.h index cd1f46c8b..750fc8d3d 100644 --- a/include/grub/dl.h +++ b/include/grub/dl.h @@ -119,7 +119,7 @@ grub_mod_fini (void) #define ATTRIBUTE_USED __unused__ #endif #define GRUB_MOD_LICENSE(license) \ - static char grub_module_license[] __attribute__ ((section (GRUB_MOD_SECTION (module_license)), ATTRIBUTE_USED)) = "LICENSE=" license; + static const char grub_module_license[] __attribute__ ((section (GRUB_MOD_SECTION (module_license)), ATTRIBUTE_USED)) = "LICENSE=" license; #define GRUB_MOD_DEP(name) \ static const char grub_module_depend_##name[] \ __attribute__((section(GRUB_MOD_SECTION(moddeps)), ATTRIBUTE_USED)) = #name From 2b79d550f331b7d275da7994eaf4d774a5abb5b3 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 9 Oct 2024 09:16:37 +0100 Subject: [PATCH 193/402] modules: Strip .llvm_addrsig sections and similar Currently GRUB modules built with Clang or GCC have several sections which we don't actually need or support. We already have a list of sections to skip in genmod.sh and this patch adds the following sections to that list (as well as a few newlines): - .note.gnu.property - .llvm* Note that the glob there won't work without a new enough linker but the failure is just reversion to the status quo. So, that's not a big problem. Signed-off-by: Peter Jones Signed-off-by: Jan Setje-Eilers Signed-off-by: Mate Kukri Reviewed-By: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/genmod.sh.in | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/grub-core/genmod.sh.in b/grub-core/genmod.sh.in index e57c4d920..337753c57 100644 --- a/grub-core/genmod.sh.in +++ b/grub-core/genmod.sh.in @@ -57,8 +57,11 @@ if test x@TARGET_APPLE_LINKER@ != x1; then @TARGET_STRIP@ --strip-unneeded \ -K grub_mod_init -K grub_mod_fini \ -K _grub_mod_init -K _grub_mod_fini \ - -R .note.gnu.gold-version -R .note.GNU-stack \ + -R .note.GNU-stack \ + -R .note.gnu.gold-version \ + -R .note.gnu.property \ -R .gnu.build.attributes \ + -R '.llvm*' \ -R .rel.gnu.build.attributes \ -R .rela.gnu.build.attributes \ -R .eh_frame -R .rela.eh_frame -R .rel.eh_frame \ From 6e2fe134ef536b178a8ae16d68fd2f55625c08fb Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 9 Oct 2024 09:16:38 +0100 Subject: [PATCH 194/402] modules: Don't allocate space for non-allocable sections Currently when loading GRUB modules we allocate space for all sections including those without SHF_ALLOC set. We then copy the sections that /do/ have SHF_ALLOC set into the allocated memory leaving some of our allocation untouched forever. Additionally, on platforms with GOT fixups and trampolines we currently compute alignment round-ups for the sections and sections with sh_size = 0. This patch removes the extra space from the allocation computation and makes the allocation computation loop skip empty sections as the loading loop does. Signed-off-by: Peter Jones Signed-off-by: Jan Setje-Eilers Signed-off-by: Mate Kukri Reviewed-By: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/kern/dl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index 0bf40caa6..37db9fab0 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -237,6 +237,9 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) i < e->e_shnum; i++, s = (const Elf_Shdr *)((const char *) s + e->e_shentsize)) { + if (s->sh_size == 0 || !(s->sh_flags & SHF_ALLOC)) + continue; + tsize = ALIGN_UP (tsize, s->sh_addralign) + s->sh_size; if (talign < s->sh_addralign) talign = s->sh_addralign; From 9fb80dd57eeca261e9a58a30a0915a2371dbaeba Mon Sep 17 00:00:00 2001 From: Mate Kukri Date: Wed, 9 Oct 2024 09:16:39 +0100 Subject: [PATCH 195/402] modules: Load module sections at page-aligned addresses Currently we load module sections at whatever alignment gcc+ld happened to dump into the ELF section header which is often less then the page size. Since NX protections are page based this alignment must be rounded up to page size on platforms supporting NX protections. This patch switches EFI platforms to load module sections at 4 KiB page-aligned addresses. It then changes the allocation size computation and the loader code in grub_dl_load_segments() to align the locations and sizes up to these boundaries and fills any added padding with zeros. All of this happens before relocations are applied, so the relocations factor that in with no change. Signed-off-by: Peter Jones Signed-off-by: Laszlo Ersek Signed-off-by: Jan Setje-Eilers Signed-off-by: Mate Kukri Reviewed-by: Daniel Kiper --- grub-core/kern/dl.c | 55 +++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index 37db9fab0..e9e2afb5b 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -33,12 +33,20 @@ #include #include +#ifdef GRUB_MACHINE_EFI +#include +#endif + /* Platforms where modules are in a readonly area of memory. */ #if defined(GRUB_MACHINE_QEMU) #define GRUB_MODULES_MACHINE_READONLY #endif - +#ifdef GRUB_MACHINE_EFI +#define DL_ALIGN GRUB_EFI_PAGE_SIZE +#else +#define DL_ALIGN 1 +#endif #pragma GCC diagnostic ignored "-Wcast-align" @@ -224,25 +232,34 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) { unsigned i; const Elf_Shdr *s; - grub_size_t tsize = 0, talign = 1; + grub_size_t tsize = 0, talign = 1, arch_addralign = 1; #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ !defined (__loongarch__) grub_size_t tramp; + grub_size_t tramp_align; grub_size_t got; + grub_size_t got_align; grub_err_t err; #endif char *ptr; + arch_addralign = DL_ALIGN; + for (i = 0, s = (const Elf_Shdr *)((const char *) e + e->e_shoff); i < e->e_shnum; i++, s = (const Elf_Shdr *)((const char *) s + e->e_shentsize)) { + grub_size_t sh_addralign; + grub_size_t sh_size; + if (s->sh_size == 0 || !(s->sh_flags & SHF_ALLOC)) continue; - tsize = ALIGN_UP (tsize, s->sh_addralign) + s->sh_size; - if (talign < s->sh_addralign) - talign = s->sh_addralign; + sh_addralign = ALIGN_UP (s->sh_addralign, arch_addralign); + sh_size = ALIGN_UP (s->sh_size, sh_addralign); + + tsize = ALIGN_UP (tsize, sh_addralign) + sh_size; + talign = grub_max (talign, sh_addralign); } #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ @@ -250,12 +267,12 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) err = grub_arch_dl_get_tramp_got_size (e, &tramp, &got); if (err) return err; - tsize += ALIGN_UP (tramp, GRUB_ARCH_DL_TRAMP_ALIGN); - if (talign < GRUB_ARCH_DL_TRAMP_ALIGN) - talign = GRUB_ARCH_DL_TRAMP_ALIGN; - tsize += ALIGN_UP (got, GRUB_ARCH_DL_GOT_ALIGN); - if (talign < GRUB_ARCH_DL_GOT_ALIGN) - talign = GRUB_ARCH_DL_GOT_ALIGN; + tramp_align = grub_max (GRUB_ARCH_DL_TRAMP_ALIGN, arch_addralign); + tsize += ALIGN_UP (tramp, tramp_align); + talign = grub_max (talign, tramp_align); + got_align = grub_max (GRUB_ARCH_DL_GOT_ALIGN, arch_addralign); + tsize += ALIGN_UP (got, got_align); + talign = grub_max (talign, got_align); #endif #ifdef GRUB_MACHINE_EMU @@ -272,6 +289,9 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) i < e->e_shnum; i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize)) { + grub_size_t sh_addralign = ALIGN_UP (s->sh_addralign, arch_addralign); + grub_size_t sh_size = ALIGN_UP (s->sh_size, sh_addralign); + if (s->sh_flags & SHF_ALLOC) { grub_dl_segment_t seg; @@ -284,17 +304,18 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) { void *addr; - ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, s->sh_addralign); + ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, sh_addralign); addr = ptr; - ptr += s->sh_size; + ptr += sh_size; switch (s->sh_type) { case SHT_PROGBITS: grub_memcpy (addr, (char *) e + s->sh_offset, s->sh_size); + grub_memset ((char *) addr + s->sh_size, 0, sh_size - s->sh_size); break; case SHT_NOBITS: - grub_memset (addr, 0, s->sh_size); + grub_memset (addr, 0, sh_size); break; } @@ -303,7 +324,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) else seg->addr = 0; - seg->size = s->sh_size; + seg->size = sh_size; seg->section = i; seg->next = mod->segment; mod->segment = seg; @@ -311,11 +332,11 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) } #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ !defined (__loongarch__) - ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_TRAMP_ALIGN); + ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, tramp_align); mod->tramp = ptr; mod->trampptr = ptr; ptr += tramp; - ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_GOT_ALIGN); + ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, got_align); mod->got = ptr; mod->gotptr = ptr; ptr += got; From 09ca66673ad228c736b3d46f31201065373cf866 Mon Sep 17 00:00:00 2001 From: Mate Kukri Date: Wed, 9 Oct 2024 09:16:40 +0100 Subject: [PATCH 196/402] nx: Add memory attribute get/set API For NX we need to set the page access permission attributes for write and execute permissions. This patch adds two new primitives, grub_set_mem_attrs() and grub_clear_mem_attrs(), and associated constants definitions used for that purpose. For most platforms it adds a dummy implementation. On EFI platforms it implements the primitives using the EFI Memory Attribute Protocol, defined in UEFI 2.10 specification. Signed-off-by: Peter Jones Signed-off-by: Jan Setje-Eilers Signed-off-by: Mate Kukri Reviewed-by: Daniel Kiper --- grub-core/kern/efi/mm.c | 109 ++++++++++++++++++++++++++++++++++++++++ include/grub/efi/api.h | 25 +++++++++ include/grub/mm.h | 35 +++++++++++++ 3 files changed, 169 insertions(+) diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c index df7bf2869..bc97ecd47 100644 --- a/grub-core/kern/efi/mm.c +++ b/grub-core/kern/efi/mm.c @@ -689,3 +689,112 @@ grub_efi_get_ram_base(grub_addr_t *base_addr) return GRUB_ERR_NONE; } #endif + +static grub_uint64_t +grub_mem_attrs_to_uefi_mem_attrs (grub_mem_attr_t attrs) +{ + grub_efi_uint64_t ret = GRUB_EFI_MEMORY_RP | GRUB_EFI_MEMORY_RO | GRUB_EFI_MEMORY_XP; + + if (attrs & GRUB_MEM_ATTR_R) + ret &= ~GRUB_EFI_MEMORY_RP; + + if (attrs & GRUB_MEM_ATTR_W) + ret &= ~GRUB_EFI_MEMORY_RO; + + if (attrs & GRUB_MEM_ATTR_X) + ret &= ~GRUB_EFI_MEMORY_XP; + + return ret; +} + +static grub_mem_attr_t +uefi_mem_attrs_to_grub_mem_attrs (grub_efi_uint64_t attrs) +{ + grub_mem_attr_t ret = GRUB_MEM_ATTR_R | GRUB_MEM_ATTR_W | GRUB_MEM_ATTR_X; + + if (attrs & GRUB_EFI_MEMORY_RP) + ret &= ~GRUB_MEM_ATTR_R; + + if (attrs & GRUB_EFI_MEMORY_RO) + ret &= ~GRUB_MEM_ATTR_W; + + if (attrs & GRUB_EFI_MEMORY_XP) + ret &= ~GRUB_MEM_ATTR_X; + + return ret; +} + +grub_err_t +grub_get_mem_attrs (grub_addr_t addr, grub_size_t size, grub_mem_attr_t *attrs) +{ + grub_efi_memory_attribute_protocol_t *proto; + grub_efi_physical_address_t physaddr = addr; + static grub_guid_t protocol_guid = GRUB_EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID; + grub_efi_status_t efi_status; + grub_efi_uint64_t efi_attrs; + + if (physaddr & (GRUB_EFI_PAGE_SIZE - 1) || size & (GRUB_EFI_PAGE_SIZE - 1) || size == 0 || attrs == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "%s() called with invalid arguments", __FUNCTION__); + + proto = grub_efi_locate_protocol (&protocol_guid, 0); + if (proto == NULL) + { + /* No protocol -> do nothing, all memory is RWX in boot services */ + *attrs = GRUB_MEM_ATTR_R | GRUB_MEM_ATTR_W | GRUB_MEM_ATTR_X; + return GRUB_ERR_NONE; + } + + efi_status = proto->get_memory_attributes (proto, physaddr, size, &efi_attrs); + if (efi_status != GRUB_EFI_SUCCESS) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "%s() called with invalid arguments", __FUNCTION__); + + *attrs = uefi_mem_attrs_to_grub_mem_attrs (efi_attrs); + + grub_dprintf ("nx", "get 0x%" PRIxGRUB_ADDR "-0x%" PRIxGRUB_ADDR ":%c%c%c\n", + addr, addr + size - 1, + (*attrs & GRUB_MEM_ATTR_R) ? 'r' : '-', + (*attrs & GRUB_MEM_ATTR_W) ? 'w' : '-', + (*attrs & GRUB_MEM_ATTR_X) ? 'x' : '-'); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_update_mem_attrs (grub_addr_t addr, grub_size_t size, + grub_mem_attr_t set_attrs, grub_mem_attr_t clear_attrs) +{ + grub_efi_memory_attribute_protocol_t *proto; + grub_efi_physical_address_t physaddr = addr; + static grub_guid_t protocol_guid = GRUB_EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID; + grub_efi_status_t efi_status = GRUB_EFI_SUCCESS; + grub_efi_uint64_t uefi_set_attrs, uefi_clear_attrs; + + if (physaddr & (GRUB_EFI_PAGE_SIZE - 1) || size & (GRUB_EFI_PAGE_SIZE - 1) || size == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "%s() called with invalid arguments", __FUNCTION__); + + proto = grub_efi_locate_protocol (&protocol_guid, 0); + if (proto == NULL) + /* No protocol -> do nothing, all memory is RWX in boot services */ + return GRUB_ERR_NONE; + + uefi_set_attrs = grub_mem_attrs_to_uefi_mem_attrs (set_attrs); + uefi_clear_attrs = grub_mem_attrs_to_uefi_mem_attrs (clear_attrs); + if (uefi_set_attrs) + efi_status = proto->set_memory_attributes (proto, physaddr, size, uefi_set_attrs); + if (efi_status == GRUB_EFI_SUCCESS && uefi_clear_attrs) + efi_status = proto->clear_memory_attributes (proto, physaddr, size, uefi_clear_attrs); + + if (efi_status != GRUB_EFI_SUCCESS) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "%s() called with invalid arguments", __FUNCTION__); + + grub_dprintf ("nx", "set +%s%s%s -%s%s%s on 0x%" PRIxGRUB_ADDR "-0x%" PRIxGRUB_ADDR "\n", + (set_attrs & GRUB_MEM_ATTR_R) ? "r" : "", + (set_attrs & GRUB_MEM_ATTR_W) ? "w" : "", + (set_attrs & GRUB_MEM_ATTR_X) ? "x" : "", + (clear_attrs & GRUB_MEM_ATTR_R) ? "r" : "", + (clear_attrs & GRUB_MEM_ATTR_W) ? "w" : "", + (clear_attrs & GRUB_MEM_ATTR_X) ? "x" : "", + addr, addr + size - 1); + + return GRUB_ERR_NONE; +} diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index d44d00ad7..b686e8afe 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -379,6 +379,11 @@ {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f } \ } +#define GRUB_EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID \ + { 0xf4560cf6, 0x40ec, 0x4b4a, \ + { 0xa1, 0x92, 0xbf, 0x1d, 0x57, 0xd0, 0xb1, 0x89 } \ + } + struct grub_efi_sal_system_table { grub_uint32_t signature; @@ -1815,4 +1820,24 @@ struct initrd_media_device_path { } GRUB_PACKED; typedef struct initrd_media_device_path initrd_media_device_path_t; +struct grub_efi_memory_attribute_protocol +{ + grub_efi_status_t (__grub_efi_api *get_memory_attributes) ( + struct grub_efi_memory_attribute_protocol *this, + grub_efi_physical_address_t base_address, + grub_efi_uint64_t length, + grub_efi_uint64_t *attributes); + grub_efi_status_t (__grub_efi_api *set_memory_attributes) ( + struct grub_efi_memory_attribute_protocol *this, + grub_efi_physical_address_t base_address, + grub_efi_uint64_t length, + grub_efi_uint64_t attributes); + grub_efi_status_t (__grub_efi_api *clear_memory_attributes) ( + struct grub_efi_memory_attribute_protocol *this, + grub_efi_physical_address_t base_address, + grub_efi_uint64_t length, + grub_efi_uint64_t attributes); +}; +typedef struct grub_efi_memory_attribute_protocol grub_efi_memory_attribute_protocol_t; + #endif /* ! GRUB_EFI_API_HEADER */ diff --git a/include/grub/mm.h b/include/grub/mm.h index f3bf87fa0..e1336d1c2 100644 --- a/include/grub/mm.h +++ b/include/grub/mm.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #ifndef NULL @@ -56,6 +57,40 @@ void *EXPORT_FUNC(grub_realloc) (void *ptr, grub_size_t size); void *EXPORT_FUNC(grub_memalign) (grub_size_t align, grub_size_t size); #endif +typedef grub_uint64_t grub_mem_attr_t; + +#define GRUB_MEM_ATTR_R ((grub_mem_attr_t) 0x0000000000000004) +#define GRUB_MEM_ATTR_W ((grub_mem_attr_t) 0x0000000000000002) +#define GRUB_MEM_ATTR_X ((grub_mem_attr_t) 0x0000000000000001) + +#ifdef GRUB_MACHINE_EFI +grub_err_t EXPORT_FUNC(grub_get_mem_attrs) (grub_addr_t addr, + grub_size_t size, + grub_mem_attr_t *attrs); +grub_err_t EXPORT_FUNC(grub_update_mem_attrs) (grub_addr_t addr, + grub_size_t size, + grub_mem_attr_t set_attrs, + grub_mem_attr_t clear_attrs); +#else /* !GRUB_MACHINE_EFI */ +static inline grub_err_t +grub_get_mem_attrs (grub_addr_t addr __attribute__((__unused__)), + grub_size_t size __attribute__((__unused__)), + grub_mem_attr_t *attrs) +{ + *attrs = GRUB_MEM_ATTR_R | GRUB_MEM_ATTR_W | GRUB_MEM_ATTR_X; + return GRUB_ERR_NONE; +} + +static inline grub_err_t +grub_update_mem_attrs (grub_addr_t addr __attribute__((__unused__)), + grub_size_t size __attribute__((__unused__)), + grub_mem_attr_t set_attrs __attribute__((__unused__)), + grub_mem_attr_t clear_attrs __attribute__((__unused__))) +{ + return GRUB_ERR_NONE; +} +#endif /* GRUB_MACHINE_EFI */ + void grub_mm_check_real (const char *file, int line); #define grub_mm_check() grub_mm_check_real (GRUB_FILE, __LINE__); From 94649c02670dae12c188cae471b9ae246b48f428 Mon Sep 17 00:00:00 2001 From: Mate Kukri Date: Wed, 9 Oct 2024 09:16:41 +0100 Subject: [PATCH 197/402] nx: Set page permissions for loaded modules For NX we need to set write and executable permissions on the sections of GRUB modules when we load them. All allocatable sections are marked readable. In addition: - SHF_WRITE sections are marked as writable, - and SHF_EXECINSTR sections are marked as executable. Where relevant for the platform the tramp and GOT areas are marked non-writable. Signed-off-by: Peter Jones Signed-off-by: Robbie Harwood Signed-off-by: Laszlo Ersek Signed-off-by: Jan Setje-Eilers Signed-off-by: Mate Kukri Reviewed-by: Daniel Kiper --- grub-core/kern/dl.c | 91 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index e9e2afb5b..5a66e0886 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -635,6 +635,94 @@ grub_dl_relocate_symbols (grub_dl_t mod, void *ehdr) return GRUB_ERR_NONE; } +/* Only define this on EFI to save space in core. */ +#ifdef GRUB_MACHINE_EFI +static grub_err_t +grub_dl_set_mem_attrs (grub_dl_t mod, void *ehdr) +{ + unsigned i; + const Elf_Shdr *s; + const Elf_Ehdr *e = ehdr; + grub_err_t err; +#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ + !defined (__loongarch__) + grub_size_t arch_addralign = GRUB_DL_ALIGN; + grub_addr_t tgaddr; + grub_size_t tgsz; +#endif + + for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff); + i < e->e_shnum; + i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize)) + { + grub_dl_segment_t seg; + grub_uint64_t set_attrs = GRUB_MEM_ATTR_R; + grub_uint64_t clear_attrs = GRUB_MEM_ATTR_W | GRUB_MEM_ATTR_X; + + for (seg = mod->segment; seg; seg = seg->next) + /* Does this ELF section's index match GRUB DL segment? */ + if (seg->section == s->sh_info) + break; + + /* No GRUB DL segment found for this ELF section, skip it. */ + if (!seg) + continue; + + if (seg->size == 0 || !(s->sh_flags & SHF_ALLOC)) + continue; + + if (s->sh_flags & SHF_WRITE) + { + set_attrs |= GRUB_MEM_ATTR_W; + clear_attrs &= ~GRUB_MEM_ATTR_W; + } + + if (s->sh_flags & SHF_EXECINSTR) + { + set_attrs |= GRUB_MEM_ATTR_X; + clear_attrs &= ~GRUB_MEM_ATTR_X; + } + + err = grub_update_mem_attrs ((grub_addr_t) seg->addr, seg->size, + set_attrs, clear_attrs); + if (err != GRUB_ERR_NONE) + return err; + } + +#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ + !defined (__loongarch__) + tgaddr = grub_min ((grub_addr_t) mod->tramp, (grub_addr_t) mod->got); + tgsz = grub_max ((grub_addr_t) mod->trampptr, (grub_addr_t) mod->gotptr) - tgaddr; + + if (tgsz) + { + tgsz = ALIGN_UP (tgsz, arch_addralign); + + if (tgaddr < (grub_addr_t) mod->base || + tgsz > (grub_addr_t) -1 - tgaddr || + tgaddr + tgsz > (grub_addr_t) mod->base + mod->sz) + return grub_error (GRUB_ERR_BUG, + "BUG: trying to protect pages outside of module " + "allocation (\"%s\"): module base %p, size 0x%" + PRIxGRUB_SIZE "; tramp/GOT base 0x%" PRIxGRUB_ADDR + ", size 0x%" PRIxGRUB_SIZE, + mod->name, mod->base, mod->sz, tgaddr, tgsz); + err = grub_update_mem_attrs (tgaddr, tgsz, GRUB_MEM_ATTR_R | GRUB_MEM_ATTR_X, GRUB_MEM_ATTR_W); + if (err != GRUB_ERR_NONE) + return err; + } +#endif + + return GRUB_ERR_NONE; +} +#else +static grub_err_t +grub_dl_set_mem_attrs (grub_dl_t mod __attribute__ ((unused)), void *ehdr __attribute__ ((unused))) +{ + return GRUB_ERR_NONE; +} +#endif + /* Load a module from core memory. */ grub_dl_t grub_dl_load_core_noinit (void *addr, grub_size_t size) @@ -681,7 +769,8 @@ grub_dl_load_core_noinit (void *addr, grub_size_t size) || grub_dl_resolve_dependencies (mod, e) || grub_dl_load_segments (mod, e) || grub_dl_resolve_symbols (mod, e) - || grub_dl_relocate_symbols (mod, e)) + || grub_dl_relocate_symbols (mod, e) + || grub_dl_set_mem_attrs (mod, e)) { mod->fini = 0; grub_dl_unload (mod); From f5bb766e688b1180e0ace3417d4947a934957a31 Mon Sep 17 00:00:00 2001 From: Mate Kukri Date: Wed, 9 Oct 2024 09:16:42 +0100 Subject: [PATCH 198/402] nx: Set the NX compatible flag for the GRUB EFI images For NX the GRUB binary has to announce that it is compatible with the NX feature. This implies that when loading the executable GRUB image several attributes are true: - the binary doesn't need an executable stack, - the binary doesn't need sections to be both executable and writable, - the binary knows how to use the EFI Memory Attributes Protocol on code it is loading. This patch: - adds a definition for the PE DLL Characteristics flag GRUB_PE32_NX_COMPAT, - changes grub-mkimage to set that flag. Signed-off-by: Peter Jones Signed-off-by: Jan Setje-Eilers Signed-off-by: Mate Kukri Reviewed-by: Daniel Kiper --- include/grub/efi/pe32.h | 2 ++ util/mkimage.c | 1 + 2 files changed, 3 insertions(+) diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h index 4e6e9d254..9887e14b2 100644 --- a/include/grub/efi/pe32.h +++ b/include/grub/efi/pe32.h @@ -231,6 +231,8 @@ struct grub_pe64_optional_header #define GRUB_PE32_SUBSYSTEM_EFI_APPLICATION 10 +#define GRUB_PE32_NX_COMPAT 0x0100 + #define GRUB_PE32_NUM_DATA_DIRECTORIES 16 struct grub_pe32_section_table diff --git a/util/mkimage.c b/util/mkimage.c index 8c5660825..845e084e0 100644 --- a/util/mkimage.c +++ b/util/mkimage.c @@ -1417,6 +1417,7 @@ grub_install_generate_image (const char *dir, const char *prefix, #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdangling-pointer" #endif + PE_OHDR (o32, o64, dll_characteristics) = grub_host_to_target16 (GRUB_PE32_NX_COMPAT); PE_OHDR (o32, o64, header_size) = grub_host_to_target32 (header_size); PE_OHDR (o32, o64, entry_addr) = grub_host_to_target32 (layout.start_address); PE_OHDR (o32, o64, image_base) = 0; From 31de991dee02e045fdec2fc1ef32b86a3f3048d5 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 16 Oct 2024 16:20:24 +1100 Subject: [PATCH 199/402] kern/acpi: Fix out of bounds access in grub_acpi_xsdt_find_table() The calculation of the size of the table was incorrect (copy/pasta from grub_acpi_rsdt_find_table() I assume...). The entries are 64-bit long. This causes us to access beyond the end of the table which is causing crashes during boot on some systems. Typically this is causing a crash on VMWare when using UEFI and enabling serial autodetection, as grub_acpi_find_table (GRUB_ACPI_SPCR_SIGNATURE); will goes past the end of the table (the SPCR table doesn't exits). Signed-off-by: Benjamin Herrenschmidt Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper Reviewed-by: Ross Philipson Tested-by: Renata Ravanelli --- grub-core/kern/acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/kern/acpi.c b/grub-core/kern/acpi.c index 48ded4e2e..8ff0835d5 100644 --- a/grub-core/kern/acpi.c +++ b/grub-core/kern/acpi.c @@ -75,7 +75,7 @@ grub_acpi_xsdt_find_table (struct grub_acpi_table_header *xsdt, const char *sig) return 0; ptr = (grub_unaligned_uint64_t *) (xsdt + 1); - s = (xsdt->length - sizeof (*xsdt)) / sizeof (grub_uint32_t); + s = (xsdt->length - sizeof (*xsdt)) / sizeof (grub_uint64_t); for (; s; s--, ptr++) { struct grub_acpi_table_header *tbl; From 337cb248625f00fd94f80d50a4f22acc9b2f0822 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Wed, 16 Oct 2024 15:04:17 +0200 Subject: [PATCH 200/402] nx: Rename GRUB_DL_ALIGN to DL_ALIGN Rename has been skipped by mistake in the original commit. Fixes: 94649c026 (nx: Set page permissions for loaded modules) Signed-off-by: Daniel Kiper Tested-by: Sudeep Holla Reviewed-by: Ross Philipson --- grub-core/kern/dl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index 5a66e0886..8ad015b07 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -646,7 +646,7 @@ grub_dl_set_mem_attrs (grub_dl_t mod, void *ehdr) grub_err_t err; #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ !defined (__loongarch__) - grub_size_t arch_addralign = GRUB_DL_ALIGN; + grub_size_t arch_addralign = DL_ALIGN; grub_addr_t tgaddr; grub_size_t tgsz; #endif From f26b39860d32e2b9edb30370349e40b1ac7055c8 Mon Sep 17 00:00:00 2001 From: Leo Sandoval Date: Wed, 16 Oct 2024 11:54:38 -0600 Subject: [PATCH 201/402] commands/legacycfg: Avoid closing file twice An internal (at Red Hat) static soure code scan detected an use-after-free scenario: Error: USE_AFTER_FREE (CWE-416): grub-2.06/grub-core/commands/legacycfg.c:194: freed_arg: "grub_file_close" frees "file". grub-2.06/grub-core/commands/legacycfg.c:201: deref_arg: Calling "grub_file_close" dereferences freed pointer "file". # 199| if (!args) # 200| { # 201|-> grub_file_close (file); # 202| grub_free (suffix); # 203| grub_free (entrysrc); So, remove the extra file close call. Signed-off-by: Leo Sandoval Reviewed-by: Daniel Kiper --- grub-core/commands/legacycfg.c | 1 - 1 file changed, 1 deletion(-) diff --git a/grub-core/commands/legacycfg.c b/grub-core/commands/legacycfg.c index e9e9d94ef..3bf9fe2e4 100644 --- a/grub-core/commands/legacycfg.c +++ b/grub-core/commands/legacycfg.c @@ -198,7 +198,6 @@ legacy_file (const char *filename) const char **args = grub_malloc (sizeof (args[0])); if (!args) { - grub_file_close (file); grub_free (suffix); grub_free (entrysrc); return grub_errno; From f97d4618a57e1969e8ba51869b8cd250ff24116c Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Wed, 23 Oct 2024 17:54:32 +0530 Subject: [PATCH 202/402] grub-mkimage: Create new ELF note for SBAT In order to store the SBAT data we create a new ELF note. The string ".sbat", zero-padded to 4 byte alignment, shall be entered in the name field. The string "SBAT"'s ASCII values, 0x53424154, should be entered in the type field. Signed-off-by: Daniel Axtens Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- include/grub/util/mkimage.h | 4 ++-- util/grub-mkimagexx.c | 48 +++++++++++++++++++++++++++++++++++-- util/mkimage.c | 5 ++-- 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/include/grub/util/mkimage.h b/include/grub/util/mkimage.h index 3819a6744..9d74f82c5 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 **core_img, size_t *core_size, + int note, char *sbat, 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 **core_img, size_t *core_size, + int note, char *sbat, char **core_img, size_t *core_size, Elf64_Addr target_addr, struct grub_mkimage_layout *layout); diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c index 520c4f97c..448862b2e 100644 --- a/util/grub-mkimagexx.c +++ b/util/grub-mkimagexx.c @@ -107,6 +107,14 @@ struct section_metadata const char *strtab; }; +#define GRUB_SBAT_NOTE_NAME ".sbat" +#define GRUB_SBAT_NOTE_TYPE 0x53424154 /* "SBAT" */ + +struct grub_sbat_note { + Elf32_Nhdr header; + char name[ALIGN_UP(sizeof(GRUB_SBAT_NOTE_NAME), 4)]; +}; + static int is_relocatable (const struct grub_install_image_target_desc *image_target) { @@ -208,7 +216,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 **core_img, size_t *core_size, + int note, char *sbat, char **core_img, size_t *core_size, Elf_Addr target_addr, struct grub_mkimage_layout *layout) { @@ -217,10 +225,17 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc Elf_Ehdr *ehdr; Elf_Phdr *phdr; Elf_Shdr *shdr; - int header_size, footer_size = 0; + int header_size, footer_size = 0, footer_offset = 0; int phnum = 1; int shnum = 4; int string_size = sizeof (".text") + sizeof ("mods") + 1; + char *footer; + + if (sbat) + { + phnum++; + footer_size += ALIGN_UP (sizeof (struct grub_sbat_note) + layout->sbat_size, 4); + } if (image_target->id != IMAGE_LOONGSON_ELF) phnum += 2; @@ -248,6 +263,7 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc ehdr = (void *) elf_img; phdr = (void *) (elf_img + sizeof (*ehdr)); shdr = (void *) (elf_img + sizeof (*ehdr) + phnum * sizeof (*phdr)); + footer = elf_img + program_size + header_size; memcpy (ehdr->e_ident, ELFMAG, SELFMAG); ehdr->e_ident[EI_CLASS] = ELFCLASSXX; if (!image_target->bigendian) @@ -420,6 +436,8 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc phdr->p_filesz = grub_host_to_target32 (XEN_NOTE_SIZE); phdr->p_memsz = 0; phdr->p_offset = grub_host_to_target32 (header_size + program_size); + footer = ptr; + footer_offset = XEN_NOTE_SIZE; } if (image_target->id == IMAGE_XEN_PVH) @@ -453,6 +471,8 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc phdr->p_filesz = grub_host_to_target32 (XEN_PVH_NOTE_SIZE); phdr->p_memsz = 0; phdr->p_offset = grub_host_to_target32 (header_size + program_size); + footer = ptr; + footer_offset = XEN_PVH_NOTE_SIZE; } if (note) @@ -483,6 +503,30 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc 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 = (elf_img + program_size + header_size + note_size); + footer_offset += note_size; + } + + if (sbat) + { + int note_size = ALIGN_UP (sizeof (struct grub_sbat_note) + layout->sbat_size, 4); + struct grub_sbat_note *note_ptr = (struct grub_sbat_note *) footer; + + note_ptr->header.n_namesz = grub_host_to_target32 (sizeof (GRUB_SBAT_NOTE_NAME)); + note_ptr->header.n_descsz = grub_host_to_target32 (ALIGN_UP(layout->sbat_size, 4)); + note_ptr->header.n_type = grub_host_to_target32 (GRUB_SBAT_NOTE_TYPE); + 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); } { diff --git a/util/mkimage.c b/util/mkimage.c index 845e084e0..ef2b9fae0 100644 --- a/util/mkimage.c +++ b/util/mkimage.c @@ -1814,6 +1814,7 @@ grub_install_generate_image (const char *dir, const char *prefix, case IMAGE_I386_IEEE1275: { grub_uint64_t target_addr; + char *sbat = NULL; if (image_target->id == IMAGE_LOONGSON_ELF) { if (comp == GRUB_COMPRESSION_NONE) @@ -1825,10 +1826,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, &core_img, &core_size, + grub_mkimage_generate_elf32 (image_target, note, sbat, &core_img, &core_size, target_addr, &layout); else - grub_mkimage_generate_elf64 (image_target, note, &core_img, &core_size, + grub_mkimage_generate_elf64 (image_target, note, sbat, &core_img, &core_size, target_addr, &layout); } break; From 9a9082b501f68bc1c6d68c0e30a5118f03d05ed4 Mon Sep 17 00:00:00 2001 From: Sudhakar Kuppusamy Date: Wed, 23 Oct 2024 17:54:33 +0530 Subject: [PATCH 203/402] grub-mkimage: Add SBAT metadata into ELF note for PowerPC targets The SBAT metadata is read from CSV file and transformed into an ELF note with the -s option. Signed-off-by: Daniel Axtens Signed-off-by: Sudhakar Kuppusamy Reviewed-by: Daniel Kiper --- util/mkimage.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/util/mkimage.c b/util/mkimage.c index ef2b9fae0..b46df2909 100644 --- a/util/mkimage.c +++ b/util/mkimage.c @@ -943,8 +943,8 @@ grub_install_generate_image (const char *dir, const char *prefix, total_module_size += dtb_size + sizeof (struct grub_module_header); } - if (sbat_path != NULL && image_target->id != IMAGE_EFI) - grub_util_error (_(".sbat section can be embedded into EFI images only")); + 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 (disable_shim_lock) total_module_size += sizeof (struct grub_module_header); @@ -1815,6 +1815,13 @@ grub_install_generate_image (const char *dir, const char *prefix, { grub_uint64_t target_addr; 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; + } if (image_target->id == IMAGE_LOONGSON_ELF) { if (comp == GRUB_COMPRESSION_NONE) From dd743ba42d0fd6679fd978d6bbc2f84c5cc3ea15 Mon Sep 17 00:00:00 2001 From: Valentin Gehrke Date: Wed, 30 Oct 2024 18:12:56 +0100 Subject: [PATCH 204/402] loader/multiboot: Do not add modules before successful download Multiboot modules that could not be read successfully, e.g. via network, should not be added to the list of modules to forward to the operating system that is to be booted subsequently. This patch is necessary because even if a grub.cfg checks whether or not a module was successfully downloaded, it is futile to retry a failed download as the corrupted module will be forwarded either way. Signed-off-by: Valentin Gehrke Reviewed-by: Daniel Kiper --- grub-core/loader/multiboot.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c index 94be512c4..36b27a906 100644 --- a/grub-core/loader/multiboot.c +++ b/grub-core/loader/multiboot.c @@ -420,13 +420,6 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), target = 0; } - err = GRUB_MULTIBOOT (add_module) (target, size, argc - 1, argv + 1); - if (err) - { - grub_file_close (file); - return err; - } - if (size && grub_file_read (file, module, size) != size) { grub_file_close (file); @@ -437,7 +430,8 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), } grub_file_close (file); - return GRUB_ERR_NONE; + + return GRUB_MULTIBOOT (add_module) (target, size, argc - 1, argv + 1); } static grub_command_t cmd_multiboot, cmd_module; From f48e6af1173c0d30209f7576407e587b7c9cf48c Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Wed, 30 Oct 2024 19:24:52 -0500 Subject: [PATCH 205/402] docs: Correct PXE environment variables descriptions Correct documentation for pxe_default_server, pxe_default_gatway and pxe_blksize. Only pxe_default_server is actually used (alias for net_default_server). So, capture this and remove the other two. Fixes: https://savannah.gnu.org/bugs/?54480 Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- docs/grub.texi | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index 2ea6c56d1..62dace828 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -2680,6 +2680,9 @@ The default interface's MAC address. Read-only. This is alias for the The default server used by network drives (@pxref{Device syntax}). Read-write, although setting this is only useful before opening a network device. +@item pxe_default_server +This performs the same function as @samp{net_default_server}. + @end table @@ -3310,8 +3313,6 @@ These variables have special meaning to GRUB. * net_default_server:: * pager:: * prefix:: -* pxe_blksize:: -* pxe_default_gateway:: * pxe_default_server:: * root:: * shim_lock:: @@ -3755,18 +3756,6 @@ dynamically loaded from this directory, so it must be set correctly in order for many parts of GRUB to work. -@node pxe_blksize -@subsection pxe_blksize - -@xref{Network}. - - -@node pxe_default_gateway -@subsection pxe_default_gateway - -@xref{Network}. - - @node pxe_default_server @subsection pxe_default_server From 097fd9d9a1f4e56281ee43b010c1bfe7d0b1ab79 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Wed, 30 Oct 2024 19:24:53 -0500 Subject: [PATCH 206/402] docs: Correct chainloader UEFI secure boot info Correct documentation for UEFI secure boot to remove statement that chainloader does not work with secure boot. This was fixed by the commit 6d05264 (kern/efi/sb: Add chainloaded image as shim's verifiable object). Fixes: https://savannah.gnu.org/bugs/?62004 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 62dace828..5dd4ed84f 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -8678,13 +8678,13 @@ secure boot chain. @node UEFI secure boot and shim @section UEFI secure boot and shim support -The GRUB, except the @command{chainloader} command, works with the UEFI secure -boot and the shim. This functionality is provided by the shim_lock verifier. It -is built into the @file{core.img} and is registered if the UEFI secure boot is -enabled. The @samp{shim_lock} variable is set to @samp{y} when shim_lock verifier -is registered. If it is desired to use UEFI secure boot without shim, one can -disable shim_lock by disabling shim verification with MokSbState UEFI variable -or by building grub image with @samp{--disable-shim-lock} option. +The GRUB works with UEFI secure boot and the shim. This functionality is +provided by the shim_lock verifier. It is built into the @file{core.img} and is +registered if the UEFI secure boot is enabled. The @samp{shim_lock} variable is +set to @samp{y} when shim_lock verifier is registered. If it is desired to use +UEFI secure boot without shim, one can disable shim_lock by disabling shim +verification with MokSbState UEFI variable or by building grub image with +@samp{--disable-shim-lock} option. All GRUB modules not stored in the @file{core.img}, OS kernels, ACPI tables, Device Trees, etc. have to be signed, e.g, using PGP. Additionally, the commands From 1763d83f540daa56fcb4f8359546331e25b25d56 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Wed, 30 Oct 2024 19:24:54 -0500 Subject: [PATCH 207/402] docs: Correct GRUB config file name for network boot Correct the documentation for the grub.cfg searching via network that will be done based on ethernet type, -01, which was missing, and a given MAC address. Fixes: https://savannah.gnu.org/bugs/?65152 Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- docs/grub.texi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index 5dd4ed84f..78d47f467 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -2571,7 +2571,7 @@ value corresponds to a value on the client machine. @example @group @samp{(FWPATH)}/grub.cfg-@samp{(UUID OF MACHINE)} -@samp{(FWPATH)}/grub.cfg-@samp{(MAC ADDRESS OF NIC)} +@samp{(FWPATH)}/grub.cfg-01-@samp{(MAC ADDRESS OF NIC)} @samp{(FWPATH)}/grub.cfg-@samp{(IPv4 OR IPv6 ADDRESS)} @samp{(FWPATH)}/grub.cfg @end group @@ -2596,7 +2596,7 @@ IPV4: 10.0.0.130 (0A000082) @example @group @samp{(FWPATH)}/grub.cfg-7726a678-7fc0-4853-a4f6-c85ac36a120a -@samp{(FWPATH)}/grub.cfg-52-54-00-ec-33-81 +@samp{(FWPATH)}/grub.cfg-01-52-54-00-ec-33-81 @samp{(FWPATH)}/grub.cfg-0A000082 @samp{(FWPATH)}/grub.cfg-0A00008 @samp{(FWPATH)}/grub.cfg-0A0000 From 792132c72a9074c049cd4436b96fad142c9cc65f Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Thu, 5 Sep 2024 20:37:11 -0500 Subject: [PATCH 208/402] docs: Fix incorrect and potentially confusing language and minor formatting Signed-off-by: Glenn Washburn Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- docs/grub.texi | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index 78d47f467..b03358bbf 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -4527,11 +4527,12 @@ files. @node emunet_module @section emunet -This module provides support for an emulated network card in GRUB. +This module provides support for networking in GRUB on the emu platform. @node emupci_module @section emupci -This module provides support for an emulated PCI bus in GRUB. +This module provides support for accessing the PCI bus in GRUB on the emu +platform. @node erofs_module @section erofs @@ -4773,8 +4774,8 @@ image support, and icon support. @node gfxterm_module @section gfxterm -This module provides support for displaying a graphical terminal interface from -GRUB. +This module provides support for displaying a terminal and menu interface from +GRUB using graphics mode. @node gfxterm_background_module @section gfxterm_background @@ -5094,8 +5095,7 @@ at the time of writing. @node memdisk_module @section memdisk This module provides support for a memdisk device. A memdisk is a memory mapped -emulated disk likely only possible in legacy environment such as with a legacy -BIOS operating in 16-bit mode. +emulated disk. @node memrw_module @section memrw @@ -5464,8 +5464,7 @@ like interface to some GRUB internal data. @node progress_module @section progress -This module provides support for showing file loading progress to the -terminal. +This module provides support for showing file loading progress to the terminal. @node pxe_module @section pxe From 4f6c4609174d92c8b881bc5adb7fe971dd5cee0b Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Thu, 29 Aug 2024 13:01:02 +0200 Subject: [PATCH 209/402] kern/fs: Honour file->read_hook() in grub_fs_blocklist_read() Unlike files accessed via a normal file system, the file->read_hook() is not honoured when using blocklist notation. This means that when trying to use a dedicated, 1 KiB, raw partition for the environment block and hence does something like save_env --file=(hd0,gpt9)0+2 X Y Z this fails with "sparse file not allowed", which is rather unexpected, as I've explicitly said exactly which blocks should be used. Adding a little debugging reveals that grub_file_size(file) is 1024 as expected, but total_length is 0, simply because the callback was never invoked, so blocklists is an empty list. Fix that by honouring the ->read_hook() set by the caller, also when a "file" is specified with blocklist notation. Signed-off-by: Rasmus Villemoes Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/kern/fs.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/grub-core/kern/fs.c b/grub-core/kern/fs.c index 7ad0aaf4e..80d325868 100644 --- a/grub-core/kern/fs.c +++ b/grub-core/kern/fs.c @@ -215,12 +215,15 @@ grub_fs_blocklist_read (grub_file_t file, char *buf, grub_size_t len) grub_disk_addr_t sector; grub_off_t offset; grub_ssize_t ret = 0; + grub_disk_t disk = file->device->disk; if (len > file->size - file->offset) len = file->size - file->offset; sector = (file->offset >> GRUB_DISK_SECTOR_BITS); offset = (file->offset & (GRUB_DISK_SECTOR_SIZE - 1)); + disk->read_hook = file->read_hook; + disk->read_hook_data = file->read_hook_data; for (p = file->data; p->length && len > 0; p++) { if (sector < p->length) @@ -232,9 +235,12 @@ grub_fs_blocklist_read (grub_file_t file, char *buf, grub_size_t len) >> GRUB_DISK_SECTOR_BITS) > p->length - sector) size = ((p->length - sector) << GRUB_DISK_SECTOR_BITS) - offset; - if (grub_disk_read (file->device->disk, p->offset + sector, offset, + if (grub_disk_read (disk, p->offset + sector, offset, size, buf) != GRUB_ERR_NONE) - return -1; + { + ret = -1; + break; + } ret += size; len -= size; @@ -244,6 +250,8 @@ grub_fs_blocklist_read (grub_file_t file, char *buf, grub_size_t len) else sector -= p->length; } + disk->read_hook = NULL; + disk->read_hook_data = NULL; return ret; } From c85c2b9f5ff674d65025cd8fc0327bd062d82954 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 15 Nov 2024 15:34:29 +0800 Subject: [PATCH 210/402] posix_wrap: Tweaks in preparation for libtasn1 Cc: Vladimir Serbinenko Signed-off-by: Daniel Axtens Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- grub-core/lib/posix_wrap/c-ctype.h | 114 +++++++++++++++++++++++++++ grub-core/lib/posix_wrap/limits.h | 1 + grub-core/lib/posix_wrap/stdlib.h | 8 ++ grub-core/lib/posix_wrap/string.h | 21 +++++ grub-core/lib/posix_wrap/sys/types.h | 1 + 5 files changed, 145 insertions(+) create mode 100644 grub-core/lib/posix_wrap/c-ctype.h diff --git a/grub-core/lib/posix_wrap/c-ctype.h b/grub-core/lib/posix_wrap/c-ctype.h new file mode 100644 index 000000000..5f8fc8ce3 --- /dev/null +++ b/grub-core/lib/posix_wrap/c-ctype.h @@ -0,0 +1,114 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2024 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 GRUB_POSIX_C_CTYPE_H +#define GRUB_POSIX_C_CTYPE_H 1 + +#include + +static inline bool +c_isspace (int c) +{ + return !!grub_isspace (c); +} + +static inline bool +c_isdigit (int c) +{ + return !!grub_isdigit (c); +} + +static inline bool +c_islower (int c) +{ + return !!grub_islower (c); +} + +static inline bool +c_isascii (int c) +{ + return !(c & ~0x7f); +} + +static inline bool +c_isupper (int c) +{ + return !!grub_isupper (c); +} + +static inline bool +c_isxdigit (int c) +{ + return !!grub_isxdigit (c); +} + +static inline bool +c_isprint (int c) +{ + return !!grub_isprint (c); +} + +static inline bool +c_iscntrl (int c) +{ + return !grub_isprint (c); +} + +static inline bool +c_isgraph (int c) +{ + return grub_isprint (c) && !grub_isspace (c); +} + +static inline bool +c_isalnum (int c) +{ + return grub_isalpha (c) || grub_isdigit (c); +} + +static inline bool +c_ispunct (int c) +{ + return grub_isprint (c) && !grub_isspace (c) && !c_isalnum (c); +} + +static inline bool +c_isalpha (int c) +{ + return !!grub_isalpha (c); +} + +static inline bool +c_isblank (int c) +{ + return c == ' ' || c == '\t'; +} + +static inline int +c_tolower (int c) +{ + return grub_tolower (c); +} + +static inline int +c_toupper (int c) +{ + return grub_toupper (c); +} + +#endif diff --git a/grub-core/lib/posix_wrap/limits.h b/grub-core/lib/posix_wrap/limits.h index 26918c8a0..4be7b4080 100644 --- a/grub-core/lib/posix_wrap/limits.h +++ b/grub-core/lib/posix_wrap/limits.h @@ -41,5 +41,6 @@ #define LONG_MAX GRUB_LONG_MAX #define CHAR_BIT 8 +#define WORD_BIT 32 #endif diff --git a/grub-core/lib/posix_wrap/stdlib.h b/grub-core/lib/posix_wrap/stdlib.h index f5279756a..14e4efdd0 100644 --- a/grub-core/lib/posix_wrap/stdlib.h +++ b/grub-core/lib/posix_wrap/stdlib.h @@ -64,4 +64,12 @@ abort (void) grub_abort (); } +#define strtol grub_strtol + +/* for libgcrypt */ +#define HAVE_STRTOUL +#define strtoul grub_strtoul + +#define strtoull grub_strtoull + #endif diff --git a/grub-core/lib/posix_wrap/string.h b/grub-core/lib/posix_wrap/string.h index 1adb450b5..d3e400d50 100644 --- a/grub-core/lib/posix_wrap/string.h +++ b/grub-core/lib/posix_wrap/string.h @@ -84,6 +84,27 @@ memchr (const void *s, int c, grub_size_t n) return grub_memchr (s, c, n); } +static inline char * +strncat (char *dest, const char *src, grub_size_t n) +{ + const char *end; + char *str = dest; + grub_size_t src_len; + + dest += grub_strlen (dest); + + end = grub_memchr (src, '\0', n); + if (end != NULL) + src_len = (grub_size_t) (end - src); + else + src_len = n; + + dest[src_len] = '\0'; + grub_memcpy (dest, src, src_len); + + return str; +} + #define memcmp grub_memcmp #define memcpy grub_memcpy #define memmove grub_memmove diff --git a/grub-core/lib/posix_wrap/sys/types.h b/grub-core/lib/posix_wrap/sys/types.h index eeda543c4..2f3e86549 100644 --- a/grub-core/lib/posix_wrap/sys/types.h +++ b/grub-core/lib/posix_wrap/sys/types.h @@ -50,6 +50,7 @@ typedef grub_uint8_t byte; typedef grub_addr_t uintptr_t; #define SIZEOF_UNSIGNED_LONG GRUB_CPU_SIZEOF_LONG +#define SIZEOF_UNSIGNED_LONG_INT GRUB_CPU_SIZEOF_LONG #define SIZEOF_UNSIGNED_INT 4 #define SIZEOF_UNSIGNED_LONG_LONG 8 #define SIZEOF_UNSIGNED_SHORT 2 From 9a26abbc36894ea9e9e5880afa30943770d60e8f Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 15 Nov 2024 15:34:30 +0800 Subject: [PATCH 211/402] libtasn1: Import libtasn1-4.19.0 Import a very trimmed-down set of libtasn1 files: curl -L -O https://ftp.gnu.org/gnu/libtasn1/libtasn1-4.19.0.tar.gz tar xvzf libtasn1-4.19.0.tar.gz rm -rf grub-core/lib/libtasn1 mkdir -p grub-core/lib/libtasn1/lib mkdir -p grub-core/lib/libtasn1/tests cp libtasn1-4.19.0/{README.md,COPYING} grub-core/lib/libtasn1 cp libtasn1-4.19.0/lib/{coding.c,decoding.c,element.c,element.h,errors.c,gstr.c,gstr.h,int.h,parser_aux.c,parser_aux.h,structure.c,structure.h} grub-core/libtasn1/lib cp libtasn1-4.19.0/lib/includes/libtasn1.h grub-core/lib/libtasn1 cp libtasn1-4.19.0/tests/{CVE-2018-1000654-1_asn1_tab.h,CVE-2018-1000654-2_asn1_tab.h,CVE-2018-1000654.c,object-id-decoding.c,object-id-encoding.c,octet-string.c,reproducers.c,Test_overflow.c,Test_simple.c,Test_strings.c} grub-core/lib/libtasn1/tests rm -rf libtasn1-4.19.0* Signed-off-by: Daniel Axtens Signed-off-by: Gary Lin Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- grub-core/lib/libtasn1/COPYING | 16 + grub-core/lib/libtasn1/README.md | 98 + grub-core/lib/libtasn1/lib/coding.c | 1425 ++++++++++ grub-core/lib/libtasn1/lib/decoding.c | 2501 +++++++++++++++++ grub-core/lib/libtasn1/lib/element.c | 1109 ++++++++ grub-core/lib/libtasn1/lib/element.h | 42 + grub-core/lib/libtasn1/lib/errors.c | 100 + grub-core/lib/libtasn1/lib/gstr.c | 74 + grub-core/lib/libtasn1/lib/gstr.h | 50 + grub-core/lib/libtasn1/lib/int.h | 221 ++ grub-core/lib/libtasn1/lib/parser_aux.c | 1178 ++++++++ grub-core/lib/libtasn1/lib/parser_aux.h | 172 ++ grub-core/lib/libtasn1/lib/structure.c | 1225 ++++++++ grub-core/lib/libtasn1/lib/structure.h | 46 + grub-core/lib/libtasn1/libtasn1.h | 643 +++++ .../tests/CVE-2018-1000654-1_asn1_tab.h | 32 + .../tests/CVE-2018-1000654-2_asn1_tab.h | 36 + .../lib/libtasn1/tests/CVE-2018-1000654.c | 72 + grub-core/lib/libtasn1/tests/Test_overflow.c | 168 ++ grub-core/lib/libtasn1/tests/Test_simple.c | 226 ++ grub-core/lib/libtasn1/tests/Test_strings.c | 156 + .../lib/libtasn1/tests/object-id-decoding.c | 121 + .../lib/libtasn1/tests/object-id-encoding.c | 133 + grub-core/lib/libtasn1/tests/octet-string.c | 230 ++ grub-core/lib/libtasn1/tests/reproducers.c | 90 + 25 files changed, 10164 insertions(+) create mode 100644 grub-core/lib/libtasn1/COPYING create mode 100644 grub-core/lib/libtasn1/README.md create mode 100644 grub-core/lib/libtasn1/lib/coding.c create mode 100644 grub-core/lib/libtasn1/lib/decoding.c create mode 100644 grub-core/lib/libtasn1/lib/element.c create mode 100644 grub-core/lib/libtasn1/lib/element.h create mode 100644 grub-core/lib/libtasn1/lib/errors.c create mode 100644 grub-core/lib/libtasn1/lib/gstr.c create mode 100644 grub-core/lib/libtasn1/lib/gstr.h create mode 100644 grub-core/lib/libtasn1/lib/int.h create mode 100644 grub-core/lib/libtasn1/lib/parser_aux.c create mode 100644 grub-core/lib/libtasn1/lib/parser_aux.h create mode 100644 grub-core/lib/libtasn1/lib/structure.c create mode 100644 grub-core/lib/libtasn1/lib/structure.h create mode 100644 grub-core/lib/libtasn1/libtasn1.h create mode 100644 grub-core/lib/libtasn1/tests/CVE-2018-1000654-1_asn1_tab.h create mode 100644 grub-core/lib/libtasn1/tests/CVE-2018-1000654-2_asn1_tab.h create mode 100644 grub-core/lib/libtasn1/tests/CVE-2018-1000654.c create mode 100644 grub-core/lib/libtasn1/tests/Test_overflow.c create mode 100644 grub-core/lib/libtasn1/tests/Test_simple.c create mode 100644 grub-core/lib/libtasn1/tests/Test_strings.c create mode 100644 grub-core/lib/libtasn1/tests/object-id-decoding.c create mode 100644 grub-core/lib/libtasn1/tests/object-id-encoding.c create mode 100644 grub-core/lib/libtasn1/tests/octet-string.c create mode 100644 grub-core/lib/libtasn1/tests/reproducers.c diff --git a/grub-core/lib/libtasn1/COPYING b/grub-core/lib/libtasn1/COPYING new file mode 100644 index 000000000..e8b3628db --- /dev/null +++ b/grub-core/lib/libtasn1/COPYING @@ -0,0 +1,16 @@ +LICENSING +========= + +The libtasn1 library is released under the GNU Lesser General Public +License (LGPL) version 2.1 or later; see [COPYING.LESSER](doc/COPYING.LESSER) +for the license terms. + +The GNU LGPL applies to the main libtasn1 library, while the +included applications library are under the GNU GPL version 3. +The libtasn1 library is located in the lib directory, while the applications +in src/. + +The documentation in doc/ is under the GNU FDL license 1.3. + +For any copyright year range specified as YYYY-ZZZZ in this package +note that the range specifies every single year in that closed interval. diff --git a/grub-core/lib/libtasn1/README.md b/grub-core/lib/libtasn1/README.md new file mode 100644 index 000000000..b0305b93e --- /dev/null +++ b/grub-core/lib/libtasn1/README.md @@ -0,0 +1,98 @@ +# Libtasn1 README -- Introduction information + +This is GNU Libtasn1, a small ASN.1 library. + +The C library (libtasn1.*) is licensed under the GNU Lesser General +Public License version 2.1 or later. See the file COPYING.LIB. + +The command line tool, self tests, examples, and other auxilliary +files, are licensed under the GNU General Public License version 3.0 +or later. See the file COPYING. + +## Building the library + +We require several tools to build the software, including: + +* [Make](https://www.gnu.org/software/make/) +* [Automake](https://www.gnu.org/software/automake/) (use 1.11.3 or later) +* [Autoconf](https://www.gnu.org/software/autoconf/) +* [Libtool](https://www.gnu.org/software/libtool/) +* [Texinfo](https://www.gnu.org/software/texinfo/) +* [help2man](http://www.gnu.org/software/help2man/) +* [Tar](https://www.gnu.org/software/tar/) +* [Gzip](https://www.gnu.org/software/gzip/) +* [bison](https://www.gnu.org/software/bison/) +* [Texlive & epsf](https://www.tug.org/texlive/) (for PDF manual) +* [GTK-DOC](https://www.gtk.org/gtk-doc/) (for API manual) +* [Git](https://git-scm.com/) +* [libabigail](https://pagure.io/libabigail/) (for abi comparison in make dist) +* [Valgrind](https://valgrind.org/) (optional) + +The required software is typically distributed with your operating +system, and the instructions for installing them differ. Here are +some hints: + +Debian/Ubuntu: +``` +sudo apt-get install make git autoconf automake libtool bison +sudo apt-get install texinfo help2man gtk-doc-tools valgrind abigail-tools +``` + +PDF manual - Debian <= stretch: +``` +sudo apt-get install texlive-generic-recommended texlive texlive-extra-utils +``` + +PDF manual - Debian >= buster: +``` +sudo apt-get install texlive-plain-generic texlive texlive-extra-utils +``` + +The next step is to run autoreconf, ./configure, etc: + +``` +$ ./bootstrap +``` + +Then build the project normally: + +``` +$ ./configure +$ make check +``` + +Happy hacking! + + +## Manual + +The manual is in the `doc/` directory of the release. + +You can also browse the manual online at: + + - https://www.gnu.org/software/libtasn1/manual/ + - https://gnutls.gitlab.io/libtasn1/manual/ + - https://gnutls.gitlab.io/libtasn1/manual/libtasn1.html + - https://gnutls.gitlab.io/libtasn1/manual/libtasn1.pdf + - https://gnutls.gitlab.io/libtasn1/reference/ + - https://gnutls.gitlab.io/libtasn1/reference/libtasn1.pdf + + +## Code coverage report + +The coverage report is at: + + - https://gnutls.gitlab.io/libtasn1/coverage + + +## Issue trackers + + - [Main issue tracker](https://gitlab.com/gnutls/libtasn1/issues) + - [oss-fuzz found issues](https://bugs.chromium.org/p/oss-fuzz/issues/list?q=libtasn1&can=2) + + +## Homepage + +The project homepage at the gnu site is at: + +https://www.gnu.org/software/libtasn1/ diff --git a/grub-core/lib/libtasn1/lib/coding.c b/grub-core/lib/libtasn1/lib/coding.c new file mode 100644 index 000000000..ea5bc370e --- /dev/null +++ b/grub-core/lib/libtasn1/lib/coding.c @@ -0,0 +1,1425 @@ +/* + * Copyright (C) 2002-2022 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +/*****************************************************/ +/* File: coding.c */ +/* Description: Functions to create a DER coding of */ +/* an ASN1 type. */ +/*****************************************************/ + +#include +#include "parser_aux.h" +#include +#include "element.h" +#include "minmax.h" +#include + +#define MAX_TAG_LEN 16 + +/******************************************************/ +/* Function : _asn1_error_description_value_not_found */ +/* Description: creates the ErrorDescription string */ +/* for the ASN1_VALUE_NOT_FOUND error. */ +/* Parameters: */ +/* node: node of the tree where the value is NULL. */ +/* ErrorDescription: string returned. */ +/* Return: */ +/******************************************************/ +static void +_asn1_error_description_value_not_found (asn1_node node, + char *ErrorDescription) +{ + + if (ErrorDescription == NULL) + return; + + Estrcpy (ErrorDescription, ":: value of element '"); + _asn1_hierarchical_name (node, ErrorDescription + strlen (ErrorDescription), + ASN1_MAX_ERROR_DESCRIPTION_SIZE - 40); + Estrcat (ErrorDescription, "' not found"); + +} + +/** + * asn1_length_der: + * @len: value to convert. + * @der: buffer to hold the returned encoding (may be %NULL). + * @der_len: number of meaningful bytes of ANS (der[0]..der[der_len-1]). + * + * Creates the DER encoding of the provided length value. + * The @der buffer must have enough room for the output. The maximum + * length this function will encode is %ASN1_MAX_LENGTH_SIZE. + * + * To know the size of the DER encoding use a %NULL value for @der. + **/ +void +asn1_length_der (unsigned long int len, unsigned char *der, int *der_len) +{ + int k; + unsigned char temp[ASN1_MAX_LENGTH_SIZE]; +#if SIZEOF_UNSIGNED_LONG_INT > 8 + len &= 0xFFFFFFFFFFFFFFFF; +#endif + + if (len < 128) + { + /* short form */ + if (der != NULL) + der[0] = (unsigned char) len; + *der_len = 1; + } + else + { + /* Long form */ + k = 0; + while (len) + { + temp[k++] = len & 0xFF; + len = len >> 8; + } + *der_len = k + 1; + if (der != NULL) + { + der[0] = ((unsigned char) k & 0x7F) + 128; + while (k--) + der[*der_len - 1 - k] = temp[k]; + } + } +} + +/******************************************************/ +/* Function : _asn1_tag_der */ +/* Description: creates the DER coding for the CLASS */ +/* and TAG parameters. */ +/* It is limited by the ASN1_MAX_TAG_SIZE variable */ +/* Parameters: */ +/* class: value to convert. */ +/* tag_value: value to convert. */ +/* ans: string returned. */ +/* ans_len: number of meaningful bytes of ANS */ +/* (ans[0]..ans[ans_len-1]). */ +/* Return: */ +/******************************************************/ +static void +_asn1_tag_der (unsigned char class, unsigned int tag_value, + unsigned char ans[ASN1_MAX_TAG_SIZE], int *ans_len) +{ + int k; + unsigned char temp[ASN1_MAX_TAG_SIZE]; + + if (tag_value < 31) + { + /* short form */ + ans[0] = (class & 0xE0) + ((unsigned char) (tag_value & 0x1F)); + *ans_len = 1; + } + else + { + /* Long form */ + ans[0] = (class & 0xE0) + 31; + k = 0; + while (tag_value != 0) + { + temp[k++] = tag_value & 0x7F; + tag_value >>= 7; + + if (k > ASN1_MAX_TAG_SIZE - 1) + break; /* will not encode larger tags */ + } + *ans_len = k + 1; + while (k--) + ans[*ans_len - 1 - k] = temp[k] + 128; + ans[*ans_len - 1] -= 128; + } +} + +/** + * asn1_octet_der: + * @str: the input data. + * @str_len: STR length (str[0]..str[*str_len-1]). + * @der: encoded string returned. + * @der_len: number of meaningful bytes of DER (der[0]..der[der_len-1]). + * + * Creates a length-value DER encoding for the input data. + * The DER encoding of the input data will be placed in the @der variable. + * + * Note that the OCTET STRING tag is not included in the output. + * + * This function does not return any value because it is expected + * that @der_len will contain enough bytes to store the string + * plus the DER encoding. The DER encoding size can be obtained using + * asn1_length_der(). + **/ +void +asn1_octet_der (const unsigned char *str, int str_len, + unsigned char *der, int *der_len) +{ + int len_len; + + if (der == NULL || str_len < 0) + return; + + asn1_length_der (str_len, der, &len_len); + memcpy (der + len_len, str, str_len); + *der_len = str_len + len_len; +} + + +/** + * asn1_encode_simple_der: + * @etype: The type of the string to be encoded (ASN1_ETYPE_) + * @str: the string data. + * @str_len: the string length + * @tl: the encoded tag and length + * @tl_len: the bytes of the @tl field + * + * Creates the DER encoding for various simple ASN.1 types like strings etc. + * It stores the tag and length in @tl, which should have space for at least + * %ASN1_MAX_TL_SIZE bytes. Initially @tl_len should contain the size of @tl. + * + * The complete DER encoding should consist of the value in @tl appended + * with the provided @str. + * + * Returns: %ASN1_SUCCESS if successful or an error value. + **/ +int +asn1_encode_simple_der (unsigned int etype, const unsigned char *str, + unsigned int str_len, unsigned char *tl, + unsigned int *tl_len) +{ + int tag_len, len_len; + unsigned tlen; + unsigned char der_tag[ASN1_MAX_TAG_SIZE]; + unsigned char der_length[ASN1_MAX_LENGTH_SIZE]; + unsigned char *p; + + if (str == NULL) + return ASN1_VALUE_NOT_VALID; + + if (ETYPE_OK (etype) == 0) + return ASN1_VALUE_NOT_VALID; + + /* doesn't handle constructed classes */ + if (ETYPE_CLASS (etype) != ASN1_CLASS_UNIVERSAL) + return ASN1_VALUE_NOT_VALID; + + _asn1_tag_der (ETYPE_CLASS (etype), ETYPE_TAG (etype), der_tag, &tag_len); + + asn1_length_der (str_len, der_length, &len_len); + + if (tag_len <= 0 || len_len <= 0) + return ASN1_VALUE_NOT_VALID; + + tlen = tag_len + len_len; + + if (*tl_len < tlen) + return ASN1_MEM_ERROR; + + p = tl; + memcpy (p, der_tag, tag_len); + p += tag_len; + memcpy (p, der_length, len_len); + + *tl_len = tlen; + + return ASN1_SUCCESS; +} + +/******************************************************/ +/* Function : _asn1_time_der */ +/* Description: creates the DER coding for a TIME */ +/* type (length included). */ +/* Parameters: */ +/* str: TIME null-terminated string. */ +/* der: string returned. */ +/* der_len: number of meaningful bytes of DER */ +/* (der[0]..der[ans_len-1]). Initially it */ +/* if must store the lenght of DER. */ +/* Return: */ +/* ASN1_MEM_ERROR when DER isn't big enough */ +/* ASN1_SUCCESS otherwise */ +/******************************************************/ +static int +_asn1_time_der (unsigned char *str, int str_len, unsigned char *der, + int *der_len) +{ + int len_len; + int max_len; + + max_len = *der_len; + + asn1_length_der (str_len, (max_len > 0) ? der : NULL, &len_len); + + if ((len_len + str_len) <= max_len) + memcpy (der + len_len, str, str_len); + *der_len = len_len + str_len; + + if ((*der_len) > max_len) + return ASN1_MEM_ERROR; + + return ASN1_SUCCESS; +} + + +/* +void +_asn1_get_utctime_der(unsigned char *der,int *der_len,unsigned char *str) +{ + int len_len,str_len; + char temp[20]; + + if(str==NULL) return; + str_len=asn1_get_length_der(der,*der_len,&len_len); + if (str_len<0) return; + memcpy(temp,der+len_len,str_len); + *der_len=str_len+len_len; + switch(str_len) + { + case 11: + temp[10]=0; + strcat(temp,"00+0000"); + break; + case 13: + temp[12]=0; + strcat(temp,"+0000"); + break; + case 15: + temp[15]=0; + memmove(temp+12,temp+10,6); + temp[10]=temp[11]='0'; + break; + case 17: + temp[17]=0; + break; + default: + return; + } + strcpy(str,temp); +} +*/ + +static void +encode_val (uint64_t val, unsigned char *der, int max_len, int *der_len) +{ + int first, k; + unsigned char bit7; + + first = 0; + for (k = sizeof (val); k >= 0; k--) + { + bit7 = (val >> (k * 7)) & 0x7F; + if (bit7 || first || !k) + { + if (k) + bit7 |= 0x80; + if (max_len > (*der_len)) + der[*der_len] = bit7; + (*der_len)++; + first = 1; + } + } +} + +/******************************************************/ +/* Function : _asn1_object_id_der */ +/* Description: creates the DER coding for an */ +/* OBJECT IDENTIFIER type (length included). */ +/* Parameters: */ +/* str: OBJECT IDENTIFIER null-terminated string. */ +/* der: string returned. */ +/* der_len: number of meaningful bytes of DER */ +/* (der[0]..der[ans_len-1]). Initially it */ +/* must store the length of DER. */ +/* Return: */ +/* ASN1_MEM_ERROR when DER isn't big enough */ +/* ASN1_SUCCESS if succesful */ +/* or an error value. */ +/******************************************************/ +static int +_asn1_object_id_der (const char *str, unsigned char *der, int *der_len) +{ + int len_len, counter, max_len; + char *temp, *n_end, *n_start; + uint64_t val, val1 = 0; + int str_len = _asn1_strlen (str); + + max_len = *der_len; + *der_len = 0; + + if (der == NULL && max_len > 0) + return ASN1_VALUE_NOT_VALID; + + temp = malloc (str_len + 2); + if (temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + memcpy (temp, str, str_len); + temp[str_len] = '.'; + temp[str_len + 1] = 0; + + counter = 0; + n_start = temp; + while ((n_end = strchr (n_start, '.'))) + { + *n_end = 0; + val = _asn1_strtou64 (n_start, NULL, 10); + counter++; + + if (counter == 1) + { + val1 = val; + } + else if (counter == 2) + { + uint64_t val0; + + if (val1 > 2) + { + free (temp); + return ASN1_VALUE_NOT_VALID; + } + else if ((val1 == 0 || val1 == 1) && val > 39) + { + free (temp); + return ASN1_VALUE_NOT_VALID; + } + + val0 = 40 * val1 + val; + encode_val (val0, der, max_len, der_len); + } + else + { + encode_val (val, der, max_len, der_len); + } + n_start = n_end + 1; + } + + asn1_length_der (*der_len, NULL, &len_len); + if (max_len >= (*der_len + len_len)) + { + memmove (der + len_len, der, *der_len); + asn1_length_der (*der_len, der, &len_len); + } + *der_len += len_len; + + free (temp); + + if (max_len < (*der_len)) + return ASN1_MEM_ERROR; + + return ASN1_SUCCESS; +} + +/** + * asn1_object_id_der: + * @str: An object identifier in numeric, dot format. + * @der: buffer to hold the returned encoding (may be %NULL). + * @der_len: initially the size of @der; will hold the final size. + * @flags: must be zero + * + * Creates the DER encoding of the provided object identifier. + * + * Returns: %ASN1_SUCCESS if DER encoding was OK, %ASN1_VALUE_NOT_VALID + * if @str is not a valid OID, %ASN1_MEM_ERROR if the @der + * vector isn't big enough and in this case @der_len will contain the + * length needed. + **/ +int +asn1_object_id_der (const char *str, unsigned char *der, int *der_len, + unsigned flags) +{ + unsigned char tag_der[MAX_TAG_LEN]; + int tag_len = 0, r; + int max_len = *der_len; + + *der_len = 0; + + _asn1_tag_der (ETYPE_CLASS (ASN1_ETYPE_OBJECT_ID), + ETYPE_TAG (ASN1_ETYPE_OBJECT_ID), tag_der, &tag_len); + + if (max_len > tag_len) + { + memcpy (der, tag_der, tag_len); + } + max_len -= tag_len; + der += tag_len; + + r = _asn1_object_id_der (str, der, &max_len); + if (r == ASN1_MEM_ERROR || r == ASN1_SUCCESS) + { + *der_len = max_len + tag_len; + } + + return r; +} + +static const unsigned char bit_mask[] = + { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80 }; + +/** + * asn1_bit_der: + * @str: BIT string. + * @bit_len: number of meaningful bits in STR. + * @der: string returned. + * @der_len: number of meaningful bytes of DER + * (der[0]..der[ans_len-1]). + * + * Creates a length-value DER encoding for the input data + * as it would have been for a BIT STRING. + * The DER encoded data will be copied in @der. + * + * Note that the BIT STRING tag is not included in the output. + * + * This function does not return any value because it is expected + * that @der_len will contain enough bytes to store the string + * plus the DER encoding. The DER encoding size can be obtained using + * asn1_length_der(). + **/ +void +asn1_bit_der (const unsigned char *str, int bit_len, + unsigned char *der, int *der_len) +{ + int len_len, len_byte, len_pad; + + if (der == NULL) + return; + + len_byte = bit_len >> 3; + len_pad = 8 - (bit_len & 7); + if (len_pad == 8) + len_pad = 0; + else + len_byte++; + asn1_length_der (len_byte + 1, der, &len_len); + der[len_len] = len_pad; + + if (str) + memcpy (der + len_len + 1, str, len_byte); + der[len_len + len_byte] &= bit_mask[len_pad]; + *der_len = len_byte + len_len + 1; +} + + +/******************************************************/ +/* Function : _asn1_complete_explicit_tag */ +/* Description: add the length coding to the EXPLICIT */ +/* tags. */ +/* Parameters: */ +/* node: pointer to the tree element. */ +/* der: string with the DER coding of the whole tree*/ +/* counter: number of meaningful bytes of DER */ +/* (der[0]..der[*counter-1]). */ +/* max_len: size of der vector */ +/* Return: */ +/* ASN1_MEM_ERROR if der vector isn't big enough, */ +/* otherwise ASN1_SUCCESS. */ +/******************************************************/ +static int +_asn1_complete_explicit_tag (asn1_node node, unsigned char *der, + int *counter, int *max_len) +{ + asn1_node p; + int is_tag_implicit, len2, len3; + unsigned char temp[SIZEOF_UNSIGNED_INT]; + + if (der == NULL && *max_len > 0) + return ASN1_VALUE_NOT_VALID; + + is_tag_implicit = 0; + + if (node->type & CONST_TAG) + { + p = node->down; + if (p == NULL) + return ASN1_DER_ERROR; + /* When there are nested tags we must complete them reverse to + the order they were created. This is because completing a tag + modifies all data within it, including the incomplete tags + which store buffer positions -- simon@josefsson.org 2002-09-06 + */ + while (p->right) + p = p->right; + while (p && p != node->down->left) + { + if (type_field (p->type) == ASN1_ETYPE_TAG) + { + if (p->type & CONST_EXPLICIT) + { + len2 = strtol (p->name, NULL, 10); + _asn1_set_name (p, NULL); + + asn1_length_der (*counter - len2, temp, &len3); + if (len3 <= (*max_len)) + { + memmove (der + len2 + len3, der + len2, + *counter - len2); + memcpy (der + len2, temp, len3); + } + *max_len -= len3; + *counter += len3; + is_tag_implicit = 0; + } + else + { /* CONST_IMPLICIT */ + if (!is_tag_implicit) + { + is_tag_implicit = 1; + } + } + } + p = p->left; + } + } + + if (*max_len < 0) + return ASN1_MEM_ERROR; + + return ASN1_SUCCESS; +} + +const tag_and_class_st _asn1_tags[] = { + [ASN1_ETYPE_GENERALSTRING] = + {ASN1_TAG_GENERALSTRING, ASN1_CLASS_UNIVERSAL, "type:GENERALSTRING"}, + [ASN1_ETYPE_NUMERIC_STRING] = + {ASN1_TAG_NUMERIC_STRING, ASN1_CLASS_UNIVERSAL, "type:NUMERIC_STR"}, + [ASN1_ETYPE_IA5_STRING] = + {ASN1_TAG_IA5_STRING, ASN1_CLASS_UNIVERSAL, "type:IA5_STR"}, + [ASN1_ETYPE_TELETEX_STRING] = + {ASN1_TAG_TELETEX_STRING, ASN1_CLASS_UNIVERSAL, "type:TELETEX_STR"}, + [ASN1_ETYPE_PRINTABLE_STRING] = + {ASN1_TAG_PRINTABLE_STRING, ASN1_CLASS_UNIVERSAL, "type:PRINTABLE_STR"}, + [ASN1_ETYPE_UNIVERSAL_STRING] = + {ASN1_TAG_UNIVERSAL_STRING, ASN1_CLASS_UNIVERSAL, "type:UNIVERSAL_STR"}, + [ASN1_ETYPE_BMP_STRING] = + {ASN1_TAG_BMP_STRING, ASN1_CLASS_UNIVERSAL, "type:BMP_STR"}, + [ASN1_ETYPE_UTF8_STRING] = + {ASN1_TAG_UTF8_STRING, ASN1_CLASS_UNIVERSAL, "type:UTF8_STR"}, + [ASN1_ETYPE_VISIBLE_STRING] = + {ASN1_TAG_VISIBLE_STRING, ASN1_CLASS_UNIVERSAL, "type:VISIBLE_STR"}, + [ASN1_ETYPE_OCTET_STRING] = + {ASN1_TAG_OCTET_STRING, ASN1_CLASS_UNIVERSAL, "type:OCT_STR"}, + [ASN1_ETYPE_BIT_STRING] = + {ASN1_TAG_BIT_STRING, ASN1_CLASS_UNIVERSAL, "type:BIT_STR"}, + [ASN1_ETYPE_OBJECT_ID] = + {ASN1_TAG_OBJECT_ID, ASN1_CLASS_UNIVERSAL, "type:OBJ_ID"}, + [ASN1_ETYPE_NULL] = {ASN1_TAG_NULL, ASN1_CLASS_UNIVERSAL, "type:NULL"}, + [ASN1_ETYPE_BOOLEAN] = + {ASN1_TAG_BOOLEAN, ASN1_CLASS_UNIVERSAL, "type:BOOLEAN"}, + [ASN1_ETYPE_INTEGER] = + {ASN1_TAG_INTEGER, ASN1_CLASS_UNIVERSAL, "type:INTEGER"}, + [ASN1_ETYPE_ENUMERATED] = + {ASN1_TAG_ENUMERATED, ASN1_CLASS_UNIVERSAL, "type:ENUMERATED"}, + [ASN1_ETYPE_SEQUENCE] = + {ASN1_TAG_SEQUENCE, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, + "type:SEQUENCE"}, + [ASN1_ETYPE_SEQUENCE_OF] = + {ASN1_TAG_SEQUENCE, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, + "type:SEQ_OF"}, + [ASN1_ETYPE_SET] = + {ASN1_TAG_SET, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, "type:SET"}, + [ASN1_ETYPE_SET_OF] = + {ASN1_TAG_SET, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, + "type:SET_OF"}, + [ASN1_ETYPE_GENERALIZED_TIME] = + {ASN1_TAG_GENERALIZEDTime, ASN1_CLASS_UNIVERSAL, "type:GENERALIZED_TIME"}, + [ASN1_ETYPE_UTC_TIME] = + {ASN1_TAG_UTCTime, ASN1_CLASS_UNIVERSAL, "type:UTC_TIME"}, +}; + +unsigned int _asn1_tags_size = sizeof (_asn1_tags) / sizeof (_asn1_tags[0]); + +/******************************************************/ +/* Function : _asn1_insert_tag_der */ +/* Description: creates the DER coding of tags of one */ +/* NODE. */ +/* Parameters: */ +/* node: pointer to the tree element. */ +/* der: string returned */ +/* counter: number of meaningful bytes of DER */ +/* (counter[0]..der[*counter-1]). */ +/* max_len: size of der vector */ +/* Return: */ +/* ASN1_GENERIC_ERROR if the type is unknown, */ +/* ASN1_MEM_ERROR if der vector isn't big enough, */ +/* otherwise ASN1_SUCCESS. */ +/******************************************************/ +static int +_asn1_insert_tag_der (asn1_node node, unsigned char *der, int *counter, + int *max_len) +{ + asn1_node p; + int tag_len, is_tag_implicit; + unsigned char class, class_implicit = + 0, temp[MAX (SIZEOF_UNSIGNED_INT * 3 + 1, LTOSTR_MAX_SIZE)]; + unsigned long tag_implicit = 0; + unsigned char tag_der[MAX_TAG_LEN]; + + is_tag_implicit = 0; + + if (node->type & CONST_TAG) + { + p = node->down; + while (p) + { + if (type_field (p->type) == ASN1_ETYPE_TAG) + { + if (p->type & CONST_APPLICATION) + class = ASN1_CLASS_APPLICATION; + else if (p->type & CONST_UNIVERSAL) + class = ASN1_CLASS_UNIVERSAL; + else if (p->type & CONST_PRIVATE) + class = ASN1_CLASS_PRIVATE; + else + class = ASN1_CLASS_CONTEXT_SPECIFIC; + + if (p->type & CONST_EXPLICIT) + { + if (is_tag_implicit) + _asn1_tag_der (class_implicit, tag_implicit, tag_der, + &tag_len); + else + _asn1_tag_der (class | ASN1_CLASS_STRUCTURED, + _asn1_strtoul (p->value, NULL, 10), + tag_der, &tag_len); + + *max_len -= tag_len; + if (der && *max_len >= 0) + memcpy (der + *counter, tag_der, tag_len); + *counter += tag_len; + + _asn1_ltostr (*counter, (char *) temp); + _asn1_set_name (p, (const char *) temp); + + is_tag_implicit = 0; + } + else + { /* CONST_IMPLICIT */ + if (!is_tag_implicit) + { + if ((type_field (node->type) == ASN1_ETYPE_SEQUENCE) || + (type_field (node->type) == ASN1_ETYPE_SEQUENCE_OF) + || (type_field (node->type) == ASN1_ETYPE_SET) + || (type_field (node->type) == ASN1_ETYPE_SET_OF)) + class |= ASN1_CLASS_STRUCTURED; + class_implicit = class; + tag_implicit = _asn1_strtoul (p->value, NULL, 10); + is_tag_implicit = 1; + } + } + } + p = p->right; + } + } + + if (is_tag_implicit) + { + _asn1_tag_der (class_implicit, tag_implicit, tag_der, &tag_len); + } + else + { + unsigned type = type_field (node->type); + switch (type) + { + CASE_HANDLED_ETYPES: + _asn1_tag_der (_asn1_tags[type].class, _asn1_tags[type].tag, + tag_der, &tag_len); + break; + case ASN1_ETYPE_TAG: + case ASN1_ETYPE_CHOICE: + case ASN1_ETYPE_ANY: + tag_len = 0; + break; + default: + return ASN1_GENERIC_ERROR; + } + } + + *max_len -= tag_len; + if (der && *max_len >= 0) + memcpy (der + *counter, tag_der, tag_len); + *counter += tag_len; + + if (*max_len < 0) + return ASN1_MEM_ERROR; + + return ASN1_SUCCESS; +} + +/******************************************************/ +/* Function : _asn1_ordering_set */ +/* Description: puts the elements of a SET type in */ +/* the correct order according to DER rules. */ +/* Parameters: */ +/* der: string with the DER coding. */ +/* node: pointer to the SET element. */ +/* Return: */ +/* ASN1_SUCCESS if successful */ +/* or an error value. */ +/******************************************************/ +static int +_asn1_ordering_set (unsigned char *der, int der_len, asn1_node node) +{ + struct vet + { + int end; + unsigned long value; + struct vet *next, *prev; + }; + + int counter, len, len2; + struct vet *first, *last, *p_vet, *p2_vet; + asn1_node p; + unsigned char class, *temp; + unsigned long tag, t; + int err; + + counter = 0; + + if (type_field (node->type) != ASN1_ETYPE_SET) + return ASN1_VALUE_NOT_VALID; + + p = node->down; + while (p && ((type_field (p->type) == ASN1_ETYPE_TAG) || + (type_field (p->type) == ASN1_ETYPE_SIZE))) + p = p->right; + + if ((p == NULL) || (p->right == NULL)) + return ASN1_SUCCESS; + + first = last = NULL; + while (p) + { + p_vet = malloc (sizeof (struct vet)); + if (p_vet == NULL) + { + err = ASN1_MEM_ALLOC_ERROR; + goto error; + } + + p_vet->next = NULL; + p_vet->prev = last; + if (first == NULL) + first = p_vet; + else + last->next = p_vet; + last = p_vet; + + /* tag value calculation */ + err = asn1_get_tag_der (der + counter, der_len - counter, &class, &len2, + &tag); + if (err != ASN1_SUCCESS) + goto error; + + t = ((unsigned int) class) << 24; + p_vet->value = t | tag; + counter += len2; + + /* extraction and length */ + len2 = asn1_get_length_der (der + counter, der_len - counter, &len); + if (len2 < 0) + { + err = ASN1_DER_ERROR; + goto error; + } + counter += len + len2; + + p_vet->end = counter; + p = p->right; + } + + p_vet = first; + + while (p_vet) + { + p2_vet = p_vet->next; + counter = 0; + while (p2_vet) + { + if (p_vet->value > p2_vet->value) + { + /* change position */ + temp = malloc (p_vet->end - counter); + if (temp == NULL) + { + err = ASN1_MEM_ALLOC_ERROR; + goto error; + } + + memcpy (temp, der + counter, p_vet->end - counter); + memcpy (der + counter, der + p_vet->end, + p2_vet->end - p_vet->end); + memcpy (der + counter + p2_vet->end - p_vet->end, temp, + p_vet->end - counter); + free (temp); + + tag = p_vet->value; + p_vet->value = p2_vet->value; + p2_vet->value = tag; + + p_vet->end = counter + (p2_vet->end - p_vet->end); + } + counter = p_vet->end; + + p2_vet = p2_vet->next; + p_vet = p_vet->next; + } + + if (p_vet != first) + p_vet->prev->next = NULL; + else + first = NULL; + free (p_vet); + p_vet = first; + } + return ASN1_SUCCESS; + +error: + while (first != NULL) + { + p_vet = first; + first = first->next; + free (p_vet); + } + return err; +} + +struct vet +{ + unsigned char *ptr; + int size; +}; + +static int +setof_compar (const void *_e1, const void *_e2) +{ + unsigned length; + const struct vet *e1 = _e1, *e2 = _e2; + int rval; + + /* The encodings of the component values of a set-of value shall + * appear in ascending order, the encodings being compared + * as octet strings with the shorter components being + * padded at their trailing end with 0-octets. + * The padding octets are for comparison purposes and + * do not appear in the encodings. + */ + length = MIN (e1->size, e2->size); + + rval = memcmp (e1->ptr, e2->ptr, length); + if (rval == 0 && e1->size != e2->size) + { + if (e1->size > e2->size) + rval = 1; + else if (e2->size > e1->size) + rval = -1; + } + + return rval; +} + +/******************************************************/ +/* Function : _asn1_ordering_set_of */ +/* Description: puts the elements of a SET OF type in */ +/* the correct order according to DER rules. */ +/* Parameters: */ +/* der: string with the DER coding. */ +/* node: pointer to the SET OF element. */ +/* Return: */ +/* ASN1_SUCCESS if successful */ +/* or an error value. */ +/******************************************************/ +static int +_asn1_ordering_set_of (unsigned char *der, int der_len, asn1_node node) +{ + int counter, len, len2; + struct vet *list = NULL, *tlist; + unsigned list_size = 0; + struct vet *p_vet; + asn1_node p; + unsigned char class; + unsigned i; + unsigned char *out = NULL; + int err; + + counter = 0; + + if (type_field (node->type) != ASN1_ETYPE_SET_OF) + return ASN1_VALUE_NOT_VALID; + + p = node->down; + while (p && ((type_field (p->type) == ASN1_ETYPE_TAG) || + (type_field (p->type) == ASN1_ETYPE_SIZE))) + p = p->right; + if (p == NULL) + return ASN1_VALUE_NOT_VALID; + p = p->right; + + if ((p == NULL) || (p->right == NULL)) + return ASN1_SUCCESS; + + while (p) + { + list_size++; + tlist = realloc (list, list_size * sizeof (struct vet)); + if (tlist == NULL) + { + err = ASN1_MEM_ALLOC_ERROR; + goto error; + } + list = tlist; + p_vet = &list[list_size - 1]; + + p_vet->ptr = der + counter; + p_vet->size = 0; + + /* extraction of tag and length */ + if (der_len - counter > 0) + { + err = asn1_get_tag_der (der + counter, der_len - counter, &class, + &len, NULL); + if (err != ASN1_SUCCESS) + goto error; + counter += len; + p_vet->size += len; + + len2 = asn1_get_length_der (der + counter, der_len - counter, &len); + if (len2 < 0) + { + err = ASN1_DER_ERROR; + goto error; + } + counter += len + len2; + p_vet->size += len + len2; + + } + else + { + err = ASN1_DER_ERROR; + goto error; + } + p = p->right; + } + + if (counter > der_len) + { + err = ASN1_DER_ERROR; + goto error; + } + + qsort (list, list_size, sizeof (struct vet), setof_compar); + + out = malloc (der_len); + if (out == NULL) + { + err = ASN1_MEM_ERROR; + goto error; + } + + /* the sum of p_vet->size == der_len */ + counter = 0; + for (i = 0; i < list_size; i++) + { + p_vet = &list[i]; + memcpy (out + counter, p_vet->ptr, p_vet->size); + counter += p_vet->size; + } + memcpy (der, out, der_len); + free (out); + + err = ASN1_SUCCESS; + +error: + free (list); + return err; +} + +/** + * asn1_der_coding: + * @element: pointer to an ASN1 element + * @name: the name of the structure you want to encode (it must be + * inside *POINTER). + * @ider: vector that will contain the DER encoding. DER must be a + * pointer to memory cells already allocated. + * @len: number of bytes of *@ider: @ider[0]..@ider[len-1], Initialy + * holds the sizeof of der vector. + * @ErrorDescription: return the error description or an empty + * string if success. + * + * Creates the DER encoding for the NAME structure (inside *POINTER + * structure). + * + * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND + * if @name is not a valid element, %ASN1_VALUE_NOT_FOUND if there + * is an element without a value, %ASN1_MEM_ERROR if the @ider + * vector isn't big enough and in this case @len will contain the + * length needed. + **/ +int +asn1_der_coding (asn1_node_const element, const char *name, void *ider, + int *len, char *ErrorDescription) +{ + asn1_node node, p, p2; + unsigned char temp[MAX (LTOSTR_MAX_SIZE, SIZEOF_UNSIGNED_LONG_INT * 3 + 1)]; + int counter, counter_old, len2, len3, move, max_len, max_len_old; + int err; + unsigned char *der = ider; + unsigned char dummy; + + if (ErrorDescription) + ErrorDescription[0] = 0; + + node = asn1_find_node (element, name); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + /* Node is now a locally allocated variable. + * That is because in some point we modify the + * structure, and I don't know why! --nmav + */ + node = _asn1_copy_structure3 (node); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + max_len = *len; + + if (der == NULL && max_len > 0) + { + err = ASN1_VALUE_NOT_VALID; + goto error; + } + + counter = 0; + move = DOWN; + p = node; + + while (1) + { + + counter_old = counter; + max_len_old = max_len; + if (move != UP) + { + p->start = counter; + err = _asn1_insert_tag_der (p, der, &counter, &max_len); + if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) + goto error; + } + switch (type_field (p->type)) + { + case ASN1_ETYPE_NULL: + max_len--; + if (der != NULL && max_len >= 0) + der[counter] = 0; + counter++; + move = RIGHT; + break; + case ASN1_ETYPE_BOOLEAN: + if ((p->type & CONST_DEFAULT) && (p->value == NULL)) + { + counter = counter_old; + max_len = max_len_old; + } + else + { + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, + ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + max_len -= 2; + if (der != NULL && max_len >= 0) + { + der[counter++] = 1; + if (p->value[0] == 'F') + der[counter++] = 0; + else + der[counter++] = 0xFF; + } + else + counter += 2; + } + move = RIGHT; + break; + case ASN1_ETYPE_INTEGER: + case ASN1_ETYPE_ENUMERATED: + if ((p->type & CONST_DEFAULT) && (p->value == NULL)) + { + counter = counter_old; + max_len = max_len_old; + } + else + { + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, + ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = asn1_get_length_der (p->value, p->value_len, &len3); + if (len2 < 0) + { + err = ASN1_DER_ERROR; + goto error; + } + max_len -= len2 + len3; + if (der != NULL && max_len >= 0) + memcpy (der + counter, p->value, len3 + len2); + counter += len3 + len2; + } + move = RIGHT; + break; + case ASN1_ETYPE_OBJECT_ID: + if ((p->type & CONST_DEFAULT) && (p->value == NULL)) + { + counter = counter_old; + max_len = max_len_old; + } + else + { + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, + ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = max_len; + err = + _asn1_object_id_der ((char *) p->value, + der ? der + counter : &dummy, &len2); + if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) + goto error; + + max_len -= len2; + counter += len2; + } + move = RIGHT; + break; + case ASN1_ETYPE_GENERALIZED_TIME: + case ASN1_ETYPE_UTC_TIME: + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = max_len; + err = + _asn1_time_der (p->value, p->value_len, + der ? der + counter : &dummy, &len2); + if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) + goto error; + + max_len -= len2; + counter += len2; + move = RIGHT; + break; + case ASN1_ETYPE_OCTET_STRING: + case ASN1_ETYPE_GENERALSTRING: + case ASN1_ETYPE_NUMERIC_STRING: + case ASN1_ETYPE_IA5_STRING: + case ASN1_ETYPE_TELETEX_STRING: + case ASN1_ETYPE_PRINTABLE_STRING: + case ASN1_ETYPE_UNIVERSAL_STRING: + case ASN1_ETYPE_BMP_STRING: + case ASN1_ETYPE_UTF8_STRING: + case ASN1_ETYPE_VISIBLE_STRING: + case ASN1_ETYPE_BIT_STRING: + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = asn1_get_length_der (p->value, p->value_len, &len3); + if (len2 < 0) + { + err = ASN1_DER_ERROR; + goto error; + } + max_len -= len2 + len3; + if (der != NULL && max_len >= 0) + memcpy (der + counter, p->value, len3 + len2); + counter += len3 + len2; + move = RIGHT; + break; + case ASN1_ETYPE_SEQUENCE: + case ASN1_ETYPE_SET: + if (move != UP) + { + p->tmp_ival = counter; + if (p->down == NULL) + { + move = UP; + continue; + } + else + { + p2 = p->down; + while (p2 && (type_field (p2->type) == ASN1_ETYPE_TAG)) + p2 = p2->right; + if (p2) + { + p = p2; + move = RIGHT; + continue; + } + move = UP; + continue; + } + } + else + { /* move==UP */ + len2 = p->tmp_ival; + p->tmp_ival = 0; + if ((type_field (p->type) == ASN1_ETYPE_SET) && (max_len >= 0)) + { + err = + _asn1_ordering_set (der ? der + len2 : &dummy, + counter - len2, p); + if (err != ASN1_SUCCESS) + goto error; + } + asn1_length_der (counter - len2, temp, &len3); + max_len -= len3; + if (der != NULL && max_len >= 0) + { + memmove (der + len2 + len3, der + len2, counter - len2); + memcpy (der + len2, temp, len3); + } + counter += len3; + move = RIGHT; + } + break; + case ASN1_ETYPE_SEQUENCE_OF: + case ASN1_ETYPE_SET_OF: + if (move != UP) + { + p->tmp_ival = counter; + p = p->down; + while ((type_field (p->type) == ASN1_ETYPE_TAG) + || (type_field (p->type) == ASN1_ETYPE_SIZE)) + p = p->right; + if (p->right) + { + p = p->right; + move = RIGHT; + continue; + } + else + p = _asn1_find_up (p); + move = UP; + } + if (move == UP) + { + len2 = p->tmp_ival; + p->tmp_ival = 0; + if ((type_field (p->type) == ASN1_ETYPE_SET_OF) + && (counter - len2 > 0) && (max_len >= 0)) + { + err = + _asn1_ordering_set_of (der ? der + len2 : &dummy, + counter - len2, p); + if (err != ASN1_SUCCESS) + goto error; + } + asn1_length_der (counter - len2, temp, &len3); + max_len -= len3; + if (der != NULL && max_len >= 0) + { + memmove (der + len2 + len3, der + len2, counter - len2); + memcpy (der + len2, temp, len3); + } + counter += len3; + move = RIGHT; + } + break; + case ASN1_ETYPE_ANY: + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = asn1_get_length_der (p->value, p->value_len, &len3); + if (len2 < 0) + { + err = ASN1_DER_ERROR; + goto error; + } + max_len -= len2; + if (der != NULL && max_len >= 0) + memcpy (der + counter, p->value + len3, len2); + counter += len2; + move = RIGHT; + break; + default: + move = (move == UP) ? RIGHT : DOWN; + break; + } + + if ((move != DOWN) && (counter != counter_old)) + { + p->end = counter - 1; + err = _asn1_complete_explicit_tag (p, der, &counter, &max_len); + if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) + goto error; + } + + if (p == node && move != DOWN) + break; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + if (move == RIGHT) + { + if (p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + *len = counter; + + if (max_len < 0) + { + err = ASN1_MEM_ERROR; + goto error; + } + + err = ASN1_SUCCESS; + +error: + asn1_delete_structure (&node); + return err; +} diff --git a/grub-core/lib/libtasn1/lib/decoding.c b/grub-core/lib/libtasn1/lib/decoding.c new file mode 100644 index 000000000..b9245c486 --- /dev/null +++ b/grub-core/lib/libtasn1/lib/decoding.c @@ -0,0 +1,2501 @@ +/* + * Copyright (C) 2002-2022 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +/*****************************************************/ +/* File: decoding.c */ +/* Description: Functions to manage DER decoding */ +/*****************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include "c-ctype.h" + +#ifdef DEBUG +# define warn() fprintf(stderr, "%s: %d\n", __func__, __LINE__) +#else +# define warn() +#endif + +#define IS_ERR(len, flags) (len < -1 || ((flags & ASN1_DECODE_FLAG_STRICT_DER) && len < 0)) + +#define HAVE_TWO(x) (x>=2?1:0) + +/* Decoding flags (dflags) used in several decoding functions. + * DECODE_FLAG_HAVE_TAG: The provided buffer includes a tag + * DECODE_FLAG_CONSTRUCTED: The provided buffer is of indefinite encoding (useful + * when no tags are present). + * DECODE_FLAG_LEVEL1: Internal flag to indicate a level of recursion for BER strings. + * DECODE_FLAG_LEVEL2: Internal flag to indicate two levels of recursion for BER strings. + * DECODE_FLAG_LEVEL3: Internal flag to indicate three levels of recursion for BER strings. + * This is the maximum levels of recursion possible to prevent stack + * exhaustion. + */ + +#define DECODE_FLAG_HAVE_TAG 1 +#define DECODE_FLAG_CONSTRUCTED (1<<1) +#define DECODE_FLAG_LEVEL1 (1<<2) +#define DECODE_FLAG_LEVEL2 (1<<3) +#define DECODE_FLAG_LEVEL3 (1<<4) + +#define DECR_LEN(l, s) do { \ + l -= s; \ + if (l < 0) { \ + warn(); \ + result = ASN1_DER_ERROR; \ + goto cleanup; \ + } \ + } while (0) + +static int +_asn1_get_indefinite_length_string (const unsigned char *der, int der_len, + int *len); + +static int +_asn1_decode_simple_ber (unsigned int etype, const unsigned char *der, + unsigned int _der_len, unsigned char **str, + unsigned int *str_len, unsigned int *ber_len, + unsigned dflags); + +static int +_asn1_decode_simple_der (unsigned int etype, const unsigned char *der, + unsigned int _der_len, const unsigned char **str, + unsigned int *str_len, unsigned dflags); + +static void +_asn1_error_description_tag_error (asn1_node node, char *ErrorDescription) +{ + + Estrcpy (ErrorDescription, ":: tag error near element '"); + _asn1_hierarchical_name (node, ErrorDescription + strlen (ErrorDescription), + ASN1_MAX_ERROR_DESCRIPTION_SIZE - 40); + Estrcat (ErrorDescription, "'"); + +} + +/** + * asn1_get_length_der: + * @der: DER data to decode. + * @der_len: Length of DER data to decode. + * @len: Output variable containing the length of the DER length field. + * + * Extract a length field from DER data. + * + * Returns: Return the decoded length value, or -1 on indefinite + * length, or -2 when the value was too big to fit in a int, or -4 + * when the decoded length value plus @len would exceed @der_len. + **/ +long +asn1_get_length_der (const unsigned char *der, int der_len, int *len) +{ + unsigned int ans; + int k, punt, sum; + + *len = 0; + if (der_len <= 0) + return 0; + + if (!(der[0] & 128)) + { + /* short form */ + *len = 1; + ans = der[0]; + } + else + { + /* Long form */ + k = der[0] & 0x7F; + punt = 1; + if (k) + { /* definite length method */ + ans = 0; + while (punt <= k && punt < der_len) + { + if (INT_MULTIPLY_OVERFLOW (ans, 256)) + return -2; + ans *= 256; + + if (INT_ADD_OVERFLOW (ans, ((unsigned) der[punt]))) + return -2; + ans += der[punt]; + punt++; + } + } + else + { /* indefinite length method */ + *len = punt; + return -1; + } + + *len = punt; + } + + sum = ans; + if (ans >= INT_MAX || INT_ADD_OVERFLOW (sum, (*len))) + return -2; + sum += *len; + + if (sum > der_len) + return -4; + + return ans; +} + +/** + * asn1_get_tag_der: + * @der: DER data to decode. + * @der_len: Length of DER data to decode. + * @cls: Output variable containing decoded class. + * @len: Output variable containing the length of the DER TAG data. + * @tag: Output variable containing the decoded tag (may be %NULL). + * + * Decode the class and TAG from DER code. + * + * Returns: Returns %ASN1_SUCCESS on success, or an error. + **/ +int +asn1_get_tag_der (const unsigned char *der, int der_len, + unsigned char *cls, int *len, unsigned long *tag) +{ + unsigned int ris; + int punt; + + if (der == NULL || der_len < 2 || len == NULL) + return ASN1_DER_ERROR; + + *cls = der[0] & 0xE0; + if ((der[0] & 0x1F) != 0x1F) + { + /* short form */ + *len = 1; + ris = der[0] & 0x1F; + } + else + { + /* Long form */ + punt = 1; + ris = 0; + while (punt < der_len && der[punt] & 128) + { + + if (INT_MULTIPLY_OVERFLOW (ris, 128)) + return ASN1_DER_ERROR; + ris *= 128; + + if (INT_ADD_OVERFLOW (ris, ((unsigned) (der[punt] & 0x7F)))) + return ASN1_DER_ERROR; + ris += (der[punt] & 0x7F); + punt++; + } + + if (punt >= der_len) + return ASN1_DER_ERROR; + + if (INT_MULTIPLY_OVERFLOW (ris, 128)) + return ASN1_DER_ERROR; + ris *= 128; + + if (INT_ADD_OVERFLOW (ris, ((unsigned) (der[punt] & 0x7F)))) + return ASN1_DER_ERROR; + ris += (der[punt] & 0x7F); + punt++; + + *len = punt; + } + + if (tag) + *tag = ris; + return ASN1_SUCCESS; +} + +/** + * asn1_get_length_ber: + * @ber: BER data to decode. + * @ber_len: Length of BER data to decode. + * @len: Output variable containing the length of the BER length field. + * + * Extract a length field from BER data. The difference to + * asn1_get_length_der() is that this function will return a length + * even if the value has indefinite encoding. + * + * Returns: Return the decoded length value, or negative value when + * the value was too big. + * + * Since: 2.0 + **/ +long +asn1_get_length_ber (const unsigned char *ber, int ber_len, int *len) +{ + int ret; + long err; + + ret = asn1_get_length_der (ber, ber_len, len); + + if (ret == -1 && ber_len > 1) + { /* indefinite length method */ + err = _asn1_get_indefinite_length_string (ber + 1, ber_len - 1, &ret); + if (err != ASN1_SUCCESS) + return -3; + } + + return ret; +} + +/** + * asn1_get_octet_der: + * @der: DER data to decode containing the OCTET SEQUENCE. + * @der_len: The length of the @der data to decode. + * @ret_len: Output variable containing the encoded length of the DER data. + * @str: Pre-allocated output buffer to put decoded OCTET SEQUENCE in. + * @str_size: Length of pre-allocated output buffer. + * @str_len: Output variable containing the length of the contents of the OCTET SEQUENCE. + * + * Extract an OCTET SEQUENCE from DER data. Note that this function + * expects the DER data past the tag field, i.e., the length and + * content octets. + * + * Returns: Returns %ASN1_SUCCESS on success, or an error. + **/ +int +asn1_get_octet_der (const unsigned char *der, int der_len, + int *ret_len, unsigned char *str, int str_size, + int *str_len) +{ + int len_len = 0; + + if (der_len <= 0) + return ASN1_GENERIC_ERROR; + + *str_len = asn1_get_length_der (der, der_len, &len_len); + + if (*str_len < 0) + return ASN1_DER_ERROR; + + *ret_len = *str_len + len_len; + if (str_size >= *str_len) + { + if (*str_len > 0 && str != NULL) + memcpy (str, der + len_len, *str_len); + } + else + { + return ASN1_MEM_ERROR; + } + + return ASN1_SUCCESS; +} + + +/*- + * _asn1_get_time_der: + * @type: %ASN1_ETYPE_GENERALIZED_TIME or %ASN1_ETYPE_UTC_TIME + * @der: DER data to decode containing the time + * @der_len: Length of DER data to decode. + * @ret_len: Output variable containing the length of the DER data. + * @str: Pre-allocated output buffer to put the textual time in. + * @str_size: Length of pre-allocated output buffer. + * @flags: Zero or %ASN1_DECODE_FLAG_STRICT_DER + * + * Performs basic checks in the DER encoded time object and returns its textual form. + * The textual form will be in the YYYYMMDD000000Z format for GeneralizedTime + * and YYMMDD000000Z for UTCTime. + * + * Returns: %ASN1_SUCCESS on success, or an error. + -*/ +static int +_asn1_get_time_der (unsigned type, const unsigned char *der, int der_len, + int *ret_len, char *str, int str_size, unsigned flags) +{ + int len_len, str_len; + unsigned i; + unsigned sign_count = 0; + unsigned dot_count = 0; + const unsigned char *p; + + if (der_len <= 0 || str == NULL) + return ASN1_DER_ERROR; + + str_len = asn1_get_length_der (der, der_len, &len_len); + if (str_len <= 0 || str_size < str_len) + return ASN1_DER_ERROR; + + /* perform some sanity checks on the data */ + if (str_len < 8) + { + warn (); + return ASN1_TIME_ENCODING_ERROR; + } + + if ((flags & ASN1_DECODE_FLAG_STRICT_DER) + && !(flags & ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME)) + { + p = &der[len_len]; + for (i = 0; i < (unsigned) (str_len - 1); i++) + { + if (c_isdigit (p[i]) == 0) + { + if (type == ASN1_ETYPE_GENERALIZED_TIME) + { + /* tolerate lax encodings */ + if (p[i] == '.' && dot_count == 0) + { + dot_count++; + continue; + } + + /* This is not really valid DER, but there are + * structures using that */ + if (!(flags & ASN1_DECODE_FLAG_STRICT_DER) && + (p[i] == '+' || p[i] == '-') && sign_count == 0) + { + sign_count++; + continue; + } + } + + warn (); + return ASN1_TIME_ENCODING_ERROR; + } + } + + if (sign_count == 0 && p[str_len - 1] != 'Z') + { + warn (); + return ASN1_TIME_ENCODING_ERROR; + } + } + memcpy (str, der + len_len, str_len); + str[str_len] = 0; + *ret_len = str_len + len_len; + + return ASN1_SUCCESS; +} + +/** + * asn1_get_object_id_der: + * @der: DER data to decode containing the OBJECT IDENTIFIER + * @der_len: Length of DER data to decode. + * @ret_len: Output variable containing the length of the DER data. + * @str: Pre-allocated output buffer to put the textual object id in. + * @str_size: Length of pre-allocated output buffer. + * + * Converts a DER encoded object identifier to its textual form. This + * function expects the DER object identifier without the tag. + * + * Returns: %ASN1_SUCCESS on success, or an error. + **/ +int +asn1_get_object_id_der (const unsigned char *der, int der_len, int *ret_len, + char *str, int str_size) +{ + int len_len, len, k; + int leading, parsed; + char temp[LTOSTR_MAX_SIZE]; + uint64_t val, val1, val0; + + *ret_len = 0; + if (str && str_size > 0) + str[0] = 0; /* no oid */ + + if (str == NULL || der_len <= 0) + return ASN1_GENERIC_ERROR; + + len = asn1_get_length_der (der, der_len, &len_len); + + if (len <= 0 || len + len_len > der_len) + return ASN1_DER_ERROR; + + /* leading octet can never be 0x80 */ + if (der[len_len] == 0x80) + return ASN1_DER_ERROR; + + val0 = 0; + + for (k = 0; k < len; k++) + { + if (INT_LEFT_SHIFT_OVERFLOW (val0, 7)) + return ASN1_DER_ERROR; + + val0 <<= 7; + val0 |= der[len_len + k] & 0x7F; + if (!(der[len_len + k] & 0x80)) + break; + } + parsed = ++k; + + /* val0 = (X*40) + Y, X={0,1,2}, Y<=39 when X={0,1} */ + /* X = val, Y = val1 */ + + /* check if X == 0 */ + val = 0; + val1 = val0; + if (val1 > 39) + { + val = 1; + val1 = val0 - 40; + if (val1 > 39) + { + val = 2; + val1 = val0 - 80; + } + } + + _asn1_str_cpy (str, str_size, _asn1_ltostr (val, temp)); + _asn1_str_cat (str, str_size, "."); + _asn1_str_cat (str, str_size, _asn1_ltostr (val1, temp)); + + val = 0; + leading = 1; + for (k = parsed; k < len; k++) + { + /* X.690 mandates that the leading byte must never be 0x80 + */ + if (leading != 0 && der[len_len + k] == 0x80) + return ASN1_DER_ERROR; + leading = 0; + + /* check for wrap around */ + if (INT_LEFT_SHIFT_OVERFLOW (val, 7)) + return ASN1_DER_ERROR; + + val = val << 7; + val |= der[len_len + k] & 0x7F; + + if (!(der[len_len + k] & 0x80)) + { + _asn1_str_cat (str, str_size, "."); + _asn1_str_cat (str, str_size, _asn1_ltostr (val, temp)); + val = 0; + leading = 1; + } + } + + if (INT_ADD_OVERFLOW (len, len_len)) + return ASN1_DER_ERROR; + + *ret_len = len + len_len; + + return ASN1_SUCCESS; +} + +/** + * asn1_get_bit_der: + * @der: DER data to decode containing the BIT SEQUENCE. + * @der_len: Length of DER data to decode. + * @ret_len: Output variable containing the length of the DER data. + * @str: Pre-allocated output buffer to put decoded BIT SEQUENCE in. + * @str_size: Length of pre-allocated output buffer. + * @bit_len: Output variable containing the size of the BIT SEQUENCE. + * + * Extract a BIT SEQUENCE from DER data. + * + * Returns: %ASN1_SUCCESS on success, or an error. + **/ +int +asn1_get_bit_der (const unsigned char *der, int der_len, + int *ret_len, unsigned char *str, int str_size, + int *bit_len) +{ + int len_len = 0, len_byte; + + if (der_len <= 0) + return ASN1_GENERIC_ERROR; + + len_byte = asn1_get_length_der (der, der_len, &len_len) - 1; + if (len_byte < 0) + return ASN1_DER_ERROR; + + *ret_len = len_byte + len_len + 1; + *bit_len = len_byte * 8 - der[len_len]; + + if (*bit_len < 0) + return ASN1_DER_ERROR; + + if (str_size >= len_byte) + { + if (len_byte > 0 && str) + memcpy (str, der + len_len + 1, len_byte); + } + else + { + return ASN1_MEM_ERROR; + } + + return ASN1_SUCCESS; +} + +/* tag_len: the total tag length (explicit+inner) + * inner_tag_len: the inner_tag length + */ +static int +_asn1_extract_tag_der (asn1_node node, const unsigned char *der, int der_len, + int *tag_len, int *inner_tag_len, unsigned flags) +{ + asn1_node p; + int counter, len2, len3, is_tag_implicit; + int result; + unsigned long tag, tag_implicit = 0; + unsigned char class, class2, class_implicit = 0; + + if (der_len <= 0) + return ASN1_GENERIC_ERROR; + + counter = is_tag_implicit = 0; + + if (node->type & CONST_TAG) + { + p = node->down; + while (p) + { + if (type_field (p->type) == ASN1_ETYPE_TAG) + { + if (p->type & CONST_APPLICATION) + class2 = ASN1_CLASS_APPLICATION; + else if (p->type & CONST_UNIVERSAL) + class2 = ASN1_CLASS_UNIVERSAL; + else if (p->type & CONST_PRIVATE) + class2 = ASN1_CLASS_PRIVATE; + else + class2 = ASN1_CLASS_CONTEXT_SPECIFIC; + + if (p->type & CONST_EXPLICIT) + { + if (asn1_get_tag_der + (der + counter, der_len, &class, &len2, + &tag) != ASN1_SUCCESS) + return ASN1_DER_ERROR; + + DECR_LEN (der_len, len2); + counter += len2; + + if (flags & ASN1_DECODE_FLAG_STRICT_DER) + len3 = + asn1_get_length_der (der + counter, der_len, &len2); + else + len3 = + asn1_get_length_ber (der + counter, der_len, &len2); + if (len3 < 0) + return ASN1_DER_ERROR; + + DECR_LEN (der_len, len2); + counter += len2; + + if (!is_tag_implicit) + { + if ((class != (class2 | ASN1_CLASS_STRUCTURED)) || + (tag != strtoul ((char *) p->value, NULL, 10))) + return ASN1_TAG_ERROR; + } + else + { /* ASN1_TAG_IMPLICIT */ + if ((class != class_implicit) || (tag != tag_implicit)) + return ASN1_TAG_ERROR; + } + is_tag_implicit = 0; + } + else + { /* ASN1_TAG_IMPLICIT */ + if (!is_tag_implicit) + { + if ((type_field (node->type) == ASN1_ETYPE_SEQUENCE) || + (type_field (node->type) == ASN1_ETYPE_SEQUENCE_OF) + || (type_field (node->type) == ASN1_ETYPE_SET) + || (type_field (node->type) == ASN1_ETYPE_SET_OF)) + class2 |= ASN1_CLASS_STRUCTURED; + class_implicit = class2; + tag_implicit = strtoul ((char *) p->value, NULL, 10); + is_tag_implicit = 1; + } + } + } + p = p->right; + } + } + + if (is_tag_implicit) + { + if (asn1_get_tag_der + (der + counter, der_len, &class, &len2, &tag) != ASN1_SUCCESS) + return ASN1_DER_ERROR; + + DECR_LEN (der_len, len2); + + if ((class != class_implicit) || (tag != tag_implicit)) + { + if (type_field (node->type) == ASN1_ETYPE_OCTET_STRING) + { + class_implicit |= ASN1_CLASS_STRUCTURED; + if ((class != class_implicit) || (tag != tag_implicit)) + return ASN1_TAG_ERROR; + } + else + return ASN1_TAG_ERROR; + } + } + else + { + unsigned type = type_field (node->type); + if (type == ASN1_ETYPE_TAG) + { + *tag_len = 0; + if (inner_tag_len) + *inner_tag_len = 0; + return ASN1_SUCCESS; + } + + if (asn1_get_tag_der + (der + counter, der_len, &class, &len2, &tag) != ASN1_SUCCESS) + return ASN1_DER_ERROR; + + DECR_LEN (der_len, len2); + + switch (type) + { + case ASN1_ETYPE_NULL: + case ASN1_ETYPE_BOOLEAN: + case ASN1_ETYPE_INTEGER: + case ASN1_ETYPE_ENUMERATED: + case ASN1_ETYPE_OBJECT_ID: + case ASN1_ETYPE_GENERALSTRING: + case ASN1_ETYPE_NUMERIC_STRING: + case ASN1_ETYPE_IA5_STRING: + case ASN1_ETYPE_TELETEX_STRING: + case ASN1_ETYPE_PRINTABLE_STRING: + case ASN1_ETYPE_UNIVERSAL_STRING: + case ASN1_ETYPE_BMP_STRING: + case ASN1_ETYPE_UTF8_STRING: + case ASN1_ETYPE_VISIBLE_STRING: + case ASN1_ETYPE_BIT_STRING: + case ASN1_ETYPE_SEQUENCE: + case ASN1_ETYPE_SEQUENCE_OF: + case ASN1_ETYPE_SET: + case ASN1_ETYPE_SET_OF: + case ASN1_ETYPE_GENERALIZED_TIME: + case ASN1_ETYPE_UTC_TIME: + if ((class != _asn1_tags[type].class) + || (tag != _asn1_tags[type].tag)) + return ASN1_DER_ERROR; + break; + + case ASN1_ETYPE_OCTET_STRING: + /* OCTET STRING is handled differently to allow + * BER encodings (structured class). */ + if (((class != ASN1_CLASS_UNIVERSAL) + && (class != (ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED))) + || (tag != ASN1_TAG_OCTET_STRING)) + return ASN1_DER_ERROR; + break; + case ASN1_ETYPE_ANY: + counter -= len2; + break; + case ASN1_ETYPE_CHOICE: + counter -= len2; + break; + default: + return ASN1_DER_ERROR; + break; + } + } + + counter += len2; + *tag_len = counter; + if (inner_tag_len) + *inner_tag_len = len2; + return ASN1_SUCCESS; + +cleanup: + return result; +} + +static int +extract_tag_der_recursive (asn1_node node, const unsigned char *der, + int der_len, int *ret_len, int *inner_len, + unsigned flags) +{ + asn1_node p; + int ris = ASN1_DER_ERROR; + + if (type_field (node->type) == ASN1_ETYPE_CHOICE) + { + p = node->down; + while (p) + { + ris = + _asn1_extract_tag_der (p, der, der_len, ret_len, inner_len, + flags); + if (ris == ASN1_SUCCESS) + break; + p = p->right; + } + + *ret_len = 0; + return ris; + } + else + return _asn1_extract_tag_der (node, der, der_len, ret_len, inner_len, + flags); +} + +static int +_asn1_delete_not_used (asn1_node node) +{ + asn1_node p, p2; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + while (p) + { + if (p->type & CONST_NOT_USED) + { + p2 = NULL; + if (p != node) + { + p2 = _asn1_find_left (p); + if (!p2) + p2 = _asn1_find_up (p); + } + asn1_delete_structure (&p); + p = p2; + } + + if (!p) + break; /* reach node */ + + if (p->down) + { + p = p->down; + } + else + { + if (p == node) + p = NULL; + else if (p->right) + p = p->right; + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == node) + { + p = NULL; + break; + } + if (p->right) + { + p = p->right; + break; + } + } + } + } + } + return ASN1_SUCCESS; +} + +static int +_asn1_get_indefinite_length_string (const unsigned char *der, + int der_len, int *len) +{ + int len2, len3, counter, indefinite; + int result; + unsigned long tag; + unsigned char class; + + counter = indefinite = 0; + + while (1) + { + if (HAVE_TWO (der_len) && (der[counter] == 0) + && (der[counter + 1] == 0)) + { + counter += 2; + DECR_LEN (der_len, 2); + + indefinite--; + if (indefinite <= 0) + break; + else + continue; + } + + if (asn1_get_tag_der + (der + counter, der_len, &class, &len2, &tag) != ASN1_SUCCESS) + return ASN1_DER_ERROR; + + DECR_LEN (der_len, len2); + counter += len2; + + len2 = asn1_get_length_der (der + counter, der_len, &len3); + if (len2 < -1) + return ASN1_DER_ERROR; + + if (len2 == -1) + { + indefinite++; + counter += 1; + DECR_LEN (der_len, 1); + } + else + { + counter += len2 + len3; + DECR_LEN (der_len, len2 + len3); + } + } + + *len = counter; + return ASN1_SUCCESS; + +cleanup: + return result; +} + +static void +delete_unneeded_choice_fields (asn1_node p) +{ + asn1_node p2; + + while (p->right) + { + p2 = p->right; + asn1_delete_structure (&p2); + } +} + + +/** + * asn1_der_decoding2 + * @element: pointer to an ASN1 structure. + * @ider: vector that contains the DER encoding. + * @max_ider_len: pointer to an integer giving the information about the + * maximal number of bytes occupied by *@ider. The real size of the DER + * encoding is returned through this pointer. + * @flags: flags controlling the behaviour of the function. + * @errorDescription: null-terminated string contains details when an + * error occurred. + * + * Fill the structure *@element with values of a DER encoding string. The + * structure must just be created with function asn1_create_element(). + * + * If %ASN1_DECODE_FLAG_ALLOW_PADDING flag is set then the function will ignore + * padding after the decoded DER data. Upon a successful return the value of + * *@max_ider_len will be set to the number of bytes decoded. + * + * If %ASN1_DECODE_FLAG_STRICT_DER flag is set then the function will + * not decode any BER-encoded elements. + * + * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND + * if @ELEMENT is %NULL, and %ASN1_TAG_ERROR or + * %ASN1_DER_ERROR if the der encoding doesn't match the structure + * name (*@ELEMENT deleted). + **/ +int +asn1_der_decoding2 (asn1_node * element, const void *ider, int *max_ider_len, + unsigned int flags, char *errorDescription) +{ + asn1_node node, p, p2, p3; + char temp[128]; + int counter, len2, len3, len4, move, ris, tlen; + struct node_tail_cache_st tcache = { NULL, NULL }; + unsigned char class; + unsigned long tag; + int tag_len; + int indefinite, result, total_len = *max_ider_len, ider_len = *max_ider_len; + int inner_tag_len; + unsigned char *ptmp; + const unsigned char *ptag; + const unsigned char *der = ider; + + node = *element; + + if (errorDescription != NULL) + errorDescription[0] = 0; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + if (node->type & CONST_OPTION) + { + result = ASN1_GENERIC_ERROR; + warn (); + goto cleanup; + } + + counter = 0; + move = DOWN; + p = node; + while (1) + { + tag_len = 0; + inner_tag_len = 0; + ris = ASN1_SUCCESS; + if (move != UP) + { + if (p->type & CONST_SET) + { + p2 = _asn1_find_up (p); + len2 = p2->tmp_ival; + if (len2 == -1) + { + if (HAVE_TWO (ider_len) && !der[counter] + && !der[counter + 1]) + { + p = p2; + move = UP; + counter += 2; + DECR_LEN (ider_len, 2); + continue; + } + } + else if (counter == len2) + { + p = p2; + move = UP; + continue; + } + else if (counter > len2) + { + result = ASN1_DER_ERROR; + warn (); + goto cleanup; + } + p2 = p2->down; + while (p2) + { + if ((p2->type & CONST_SET) && (p2->type & CONST_NOT_USED)) + { + ris = + extract_tag_der_recursive (p2, der + counter, + ider_len, &len2, NULL, + flags); + if (ris == ASN1_SUCCESS) + { + p2->type &= ~CONST_NOT_USED; + p = p2; + break; + } + } + p2 = p2->right; + } + if (p2 == NULL) + { + result = ASN1_DER_ERROR; + warn (); + goto cleanup; + } + } + + /* the position in the DER structure this starts */ + p->start = counter; + p->end = total_len - 1; + + if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT)) + { + p2 = _asn1_find_up (p); + len2 = p2->tmp_ival; + if (counter == len2) + { + if (p->right) + { + p2 = p->right; + move = RIGHT; + } + else + move = UP; + + if (p->type & CONST_OPTION) + asn1_delete_structure (&p); + + p = p2; + continue; + } + } + + if (type_field (p->type) == ASN1_ETYPE_CHOICE) + { + while (p->down) + { + ris = + extract_tag_der_recursive (p->down, der + counter, + ider_len, &len2, NULL, flags); + + if (ris == ASN1_SUCCESS) + { + delete_unneeded_choice_fields (p->down); + break; + } + else if (ris == ASN1_ERROR_TYPE_ANY) + { + result = ASN1_ERROR_TYPE_ANY; + warn (); + goto cleanup; + } + else + { + p2 = p->down; + asn1_delete_structure (&p2); + } + } + + if (p->down == NULL) + { + if (!(p->type & CONST_OPTION)) + { + result = ASN1_DER_ERROR; + warn (); + goto cleanup; + } + } + else if (type_field (p->type) != ASN1_ETYPE_CHOICE) + p = p->down; + + p->start = counter; + } + + if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT)) + { + p2 = _asn1_find_up (p); + len2 = p2->tmp_ival; + + if ((len2 != -1) && (counter > len2)) + ris = ASN1_TAG_ERROR; + } + + if (ris == ASN1_SUCCESS) + ris = + extract_tag_der_recursive (p, der + counter, ider_len, + &tag_len, &inner_tag_len, flags); + + if (ris != ASN1_SUCCESS) + { + if (p->type & CONST_OPTION) + { + p->type |= CONST_NOT_USED; + move = RIGHT; + } + else if (p->type & CONST_DEFAULT) + { + _asn1_set_value (p, NULL, 0); + move = RIGHT; + } + else + { + if (errorDescription != NULL) + _asn1_error_description_tag_error (p, errorDescription); + + result = ASN1_TAG_ERROR; + warn (); + goto cleanup; + } + } + else + { + DECR_LEN (ider_len, tag_len); + counter += tag_len; + } + } + + if (ris == ASN1_SUCCESS) + { + switch (type_field (p->type)) + { + case ASN1_ETYPE_NULL: + DECR_LEN (ider_len, 1); + if (der[counter]) + { + result = ASN1_DER_ERROR; + warn (); + goto cleanup; + } + counter++; + move = RIGHT; + break; + case ASN1_ETYPE_BOOLEAN: + DECR_LEN (ider_len, 2); + + if (der[counter++] != 1) + { + result = ASN1_DER_ERROR; + warn (); + goto cleanup; + } + if (der[counter++] == 0) + _asn1_set_value (p, "F", 1); + else + _asn1_set_value (p, "T", 1); + move = RIGHT; + break; + case ASN1_ETYPE_INTEGER: + case ASN1_ETYPE_ENUMERATED: + len2 = asn1_get_length_der (der + counter, ider_len, &len3); + if (len2 < 0) + { + result = ASN1_DER_ERROR; + warn (); + goto cleanup; + } + + DECR_LEN (ider_len, len3 + len2); + + _asn1_set_value (p, der + counter, len3 + len2); + counter += len3 + len2; + move = RIGHT; + break; + case ASN1_ETYPE_OBJECT_ID: + result = + asn1_get_object_id_der (der + counter, ider_len, &len2, + temp, sizeof (temp)); + if (result != ASN1_SUCCESS) + { + warn (); + goto cleanup; + } + + DECR_LEN (ider_len, len2); + + tlen = strlen (temp); + if (tlen > 0) + _asn1_set_value (p, temp, tlen + 1); + + counter += len2; + move = RIGHT; + break; + case ASN1_ETYPE_GENERALIZED_TIME: + case ASN1_ETYPE_UTC_TIME: + result = + _asn1_get_time_der (type_field (p->type), der + counter, + ider_len, &len2, temp, sizeof (temp) - 1, + flags); + if (result != ASN1_SUCCESS) + { + warn (); + goto cleanup; + } + + DECR_LEN (ider_len, len2); + + tlen = strlen (temp); + if (tlen > 0) + _asn1_set_value (p, temp, tlen); + + counter += len2; + move = RIGHT; + break; + case ASN1_ETYPE_OCTET_STRING: + if (counter < inner_tag_len) + { + result = ASN1_DER_ERROR; + warn (); + goto cleanup; + } + + ptag = der + counter - inner_tag_len; + if ((flags & ASN1_DECODE_FLAG_STRICT_DER) + || !(ptag[0] & ASN1_CLASS_STRUCTURED)) + { + if (ptag[0] & ASN1_CLASS_STRUCTURED) + { + result = ASN1_DER_ERROR; + warn (); + goto cleanup; + } + + len2 = asn1_get_length_der (der + counter, ider_len, &len3); + if (len2 < 0) + { + result = ASN1_DER_ERROR; + warn (); + goto cleanup; + } + + DECR_LEN (ider_len, len3 + len2); + + _asn1_set_value (p, der + counter, len3 + len2); + counter += len3 + len2; + } + else + { + unsigned dflags = 0, vlen, ber_len; + + if (ptag[0] & ASN1_CLASS_STRUCTURED) + dflags |= DECODE_FLAG_CONSTRUCTED; + + result = + _asn1_decode_simple_ber (type_field (p->type), + der + counter, ider_len, &ptmp, + &vlen, &ber_len, dflags); + if (result != ASN1_SUCCESS) + { + warn (); + goto cleanup; + } + + DECR_LEN (ider_len, ber_len); + + _asn1_set_value_lv (p, ptmp, vlen); + + counter += ber_len; + free (ptmp); + } + move = RIGHT; + break; + case ASN1_ETYPE_GENERALSTRING: + case ASN1_ETYPE_NUMERIC_STRING: + case ASN1_ETYPE_IA5_STRING: + case ASN1_ETYPE_TELETEX_STRING: + case ASN1_ETYPE_PRINTABLE_STRING: + case ASN1_ETYPE_UNIVERSAL_STRING: + case ASN1_ETYPE_BMP_STRING: + case ASN1_ETYPE_UTF8_STRING: + case ASN1_ETYPE_VISIBLE_STRING: + case ASN1_ETYPE_BIT_STRING: + len2 = asn1_get_length_der (der + counter, ider_len, &len3); + if (len2 < 0) + { + result = ASN1_DER_ERROR; + warn (); + goto cleanup; + } + + DECR_LEN (ider_len, len3 + len2); + + _asn1_set_value (p, der + counter, len3 + len2); + counter += len3 + len2; + move = RIGHT; + break; + case ASN1_ETYPE_SEQUENCE: + case ASN1_ETYPE_SET: + if (move == UP) + { + len2 = p->tmp_ival; + p->tmp_ival = 0; + if (len2 == -1) + { /* indefinite length method */ + DECR_LEN (ider_len, 2); + if ((der[counter]) || der[counter + 1]) + { + result = ASN1_DER_ERROR; + warn (); + goto cleanup; + } + counter += 2; + } + else + { /* definite length method */ + if (len2 != counter) + { + result = ASN1_DER_ERROR; + warn (); + goto cleanup; + } + } + move = RIGHT; + } + else + { /* move==DOWN || move==RIGHT */ + len3 = asn1_get_length_der (der + counter, ider_len, &len2); + if (IS_ERR (len3, flags)) + { + result = ASN1_DER_ERROR; + warn (); + goto cleanup; + } + + DECR_LEN (ider_len, len2); + counter += len2; + + if (len3 > 0) + { + p->tmp_ival = counter + len3; + move = DOWN; + } + else if (len3 == 0) + { + p2 = p->down; + while (p2) + { + if (type_field (p2->type) != ASN1_ETYPE_TAG) + { + p3 = p2->right; + asn1_delete_structure (&p2); + p2 = p3; + } + else + p2 = p2->right; + } + move = RIGHT; + } + else + { /* indefinite length method */ + p->tmp_ival = -1; + move = DOWN; + } + } + break; + case ASN1_ETYPE_SEQUENCE_OF: + case ASN1_ETYPE_SET_OF: + if (move == UP) + { + len2 = p->tmp_ival; + if (len2 == -1) + { /* indefinite length method */ + if (!HAVE_TWO (ider_len) + || ((der[counter]) || der[counter + 1])) + { + result = _asn1_append_sequence_set (p, &tcache); + if (result != 0) + { + warn (); + goto cleanup; + } + p = tcache.tail; + move = RIGHT; + continue; + } + + p->tmp_ival = 0; + tcache.tail = NULL; /* finished decoding this structure */ + tcache.head = NULL; + DECR_LEN (ider_len, 2); + counter += 2; + } + else + { /* definite length method */ + if (len2 > counter) + { + result = _asn1_append_sequence_set (p, &tcache); + if (result != 0) + { + warn (); + goto cleanup; + } + p = tcache.tail; + move = RIGHT; + continue; + } + + p->tmp_ival = 0; + tcache.tail = NULL; /* finished decoding this structure */ + tcache.head = NULL; + + if (len2 != counter) + { + result = ASN1_DER_ERROR; + warn (); + goto cleanup; + } + } + } + else + { /* move==DOWN || move==RIGHT */ + len3 = asn1_get_length_der (der + counter, ider_len, &len2); + if (IS_ERR (len3, flags)) + { + result = ASN1_DER_ERROR; + warn (); + goto cleanup; + } + + DECR_LEN (ider_len, len2); + counter += len2; + if (len3) + { + if (len3 > 0) + { /* definite length method */ + p->tmp_ival = counter + len3; + } + else + { /* indefinite length method */ + p->tmp_ival = -1; + } + + p2 = p->down; + if (p2 == NULL) + { + result = ASN1_DER_ERROR; + warn (); + goto cleanup; + } + + while ((type_field (p2->type) == ASN1_ETYPE_TAG) + || (type_field (p2->type) == ASN1_ETYPE_SIZE)) + p2 = p2->right; + if (p2->right == NULL) + { + result = _asn1_append_sequence_set (p, &tcache); + if (result != 0) + { + warn (); + goto cleanup; + } + } + p = p2; + } + } + move = RIGHT; + break; + case ASN1_ETYPE_ANY: + /* Check indefinite lenth method in an EXPLICIT TAG */ + + if (!(flags & ASN1_DECODE_FLAG_STRICT_DER) + && (p->type & CONST_TAG) && tag_len == 2 + && (der[counter - 1] == 0x80)) + indefinite = 1; + else + indefinite = 0; + + if (asn1_get_tag_der + (der + counter, ider_len, &class, &len2, + &tag) != ASN1_SUCCESS) + { + result = ASN1_DER_ERROR; + warn (); + goto cleanup; + } + + DECR_LEN (ider_len, len2); + + len4 = + asn1_get_length_der (der + counter + len2, ider_len, &len3); + if (IS_ERR (len4, flags)) + { + result = ASN1_DER_ERROR; + warn (); + goto cleanup; + } + if (len4 != -1) /* definite */ + { + len2 += len4; + + DECR_LEN (ider_len, len4 + len3); + _asn1_set_value_lv (p, der + counter, len2 + len3); + counter += len2 + len3; + } + else /* == -1 */ + { /* indefinite length */ + ider_len += len2; /* undo DECR_LEN */ + + if (counter == 0) + { + result = ASN1_DER_ERROR; + warn (); + goto cleanup; + } + + result = + _asn1_get_indefinite_length_string (der + counter, + ider_len, &len2); + if (result != ASN1_SUCCESS) + { + warn (); + goto cleanup; + } + + DECR_LEN (ider_len, len2); + _asn1_set_value_lv (p, der + counter, len2); + counter += len2; + + } + + /* Check if a couple of 0x00 are present due to an EXPLICIT TAG with + an indefinite length method. */ + if (indefinite) + { + DECR_LEN (ider_len, 2); + if (!der[counter] && !der[counter + 1]) + { + counter += 2; + } + else + { + result = ASN1_DER_ERROR; + warn (); + goto cleanup; + } + } + + move = RIGHT; + break; + default: + move = (move == UP) ? RIGHT : DOWN; + break; + } + } + + if (p) + { + p->end = counter - 1; + } + + if (p == node && move != DOWN) + break; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + if ((move == RIGHT) && !(p->type & CONST_SET)) + { + if (p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + _asn1_delete_not_used (*element); + + if ((ider_len < 0) || + (!(flags & ASN1_DECODE_FLAG_ALLOW_PADDING) && (ider_len != 0))) + { + warn (); + result = ASN1_DER_ERROR; + goto cleanup; + } + + *max_ider_len = total_len - ider_len; + + return ASN1_SUCCESS; + +cleanup: + asn1_delete_structure (element); + return result; +} + + +/** + * asn1_der_decoding: + * @element: pointer to an ASN1 structure. + * @ider: vector that contains the DER encoding. + * @ider_len: number of bytes of *@ider: @ider[0]..@ider[len-1]. + * @errorDescription: null-terminated string contains details when an + * error occurred. + * + * Fill the structure *@element with values of a DER encoding + * string. The structure must just be created with function + * asn1_create_element(). + * + * Note that the *@element variable is provided as a pointer for + * historical reasons. + * + * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND + * if @ELEMENT is %NULL, and %ASN1_TAG_ERROR or + * %ASN1_DER_ERROR if the der encoding doesn't match the structure + * name (*@ELEMENT deleted). + **/ +int +asn1_der_decoding (asn1_node * element, const void *ider, int ider_len, + char *errorDescription) +{ + return asn1_der_decoding2 (element, ider, &ider_len, 0, errorDescription); +} + +/** + * asn1_der_decoding_element: + * @structure: pointer to an ASN1 structure + * @elementName: name of the element to fill + * @ider: vector that contains the DER encoding of the whole structure. + * @len: number of bytes of *der: der[0]..der[len-1] + * @errorDescription: null-terminated string contains details when an + * error occurred. + * + * Fill the element named @ELEMENTNAME with values of a DER encoding + * string. The structure must just be created with function + * asn1_create_element(). The DER vector must contain the encoding + * string of the whole @STRUCTURE. If an error occurs during the + * decoding procedure, the *@STRUCTURE is deleted and set equal to + * %NULL. + * + * This function is deprecated and may just be an alias to asn1_der_decoding + * in future versions. Use asn1_der_decoding() instead. + * + * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND + * if ELEMENT is %NULL or @elementName == NULL, and + * %ASN1_TAG_ERROR or %ASN1_DER_ERROR if the der encoding doesn't + * match the structure @structure (*ELEMENT deleted). + **/ +int +asn1_der_decoding_element (asn1_node * structure, const char *elementName, + const void *ider, int len, char *errorDescription) +{ + return asn1_der_decoding (structure, ider, len, errorDescription); +} + +/** + * asn1_der_decoding_startEnd: + * @element: pointer to an ASN1 element + * @ider: vector that contains the DER encoding. + * @ider_len: number of bytes of *@ider: @ider[0]..@ider[len-1] + * @name_element: an element of NAME structure. + * @start: the position of the first byte of NAME_ELEMENT decoding + * (@ider[*start]) + * @end: the position of the last byte of NAME_ELEMENT decoding + * (@ider[*end]) + * + * Find the start and end point of an element in a DER encoding + * string. I mean that if you have a der encoding and you have already + * used the function asn1_der_decoding() to fill a structure, it may + * happen that you want to find the piece of string concerning an + * element of the structure. + * + * One example is the sequence "tbsCertificate" inside an X509 + * certificate. + * + * Note that since libtasn1 3.7 the @ider and @ider_len parameters + * can be omitted, if the element is already decoded using asn1_der_decoding(). + * + * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND + * if ELEMENT is %asn1_node EMPTY or @name_element is not a valid + * element, %ASN1_TAG_ERROR or %ASN1_DER_ERROR if the der encoding + * doesn't match the structure ELEMENT. + **/ +int +asn1_der_decoding_startEnd (asn1_node element, const void *ider, int ider_len, + const char *name_element, int *start, int *end) +{ + asn1_node node, node_to_find; + int result = ASN1_DER_ERROR; + + node = element; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + node_to_find = asn1_find_node (node, name_element); + + if (node_to_find == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + *start = node_to_find->start; + *end = node_to_find->end; + + if (*start == 0 && *end == 0) + { + if (ider == NULL || ider_len == 0) + return ASN1_GENERIC_ERROR; + + /* it seems asn1_der_decoding() wasn't called before. Do it now */ + result = asn1_der_decoding (&node, ider, ider_len, NULL); + if (result != ASN1_SUCCESS) + { + warn (); + return result; + } + + node_to_find = asn1_find_node (node, name_element); + if (node_to_find == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + *start = node_to_find->start; + *end = node_to_find->end; + } + + if (*end < *start) + return ASN1_GENERIC_ERROR; + + return ASN1_SUCCESS; +} + +/** + * asn1_expand_any_defined_by: + * @definitions: ASN1 definitions + * @element: pointer to an ASN1 structure + * + * Expands every "ANY DEFINED BY" element of a structure created from + * a DER decoding process (asn1_der_decoding function). The element + * ANY must be defined by an OBJECT IDENTIFIER. The type used to + * expand the element ANY is the first one following the definition of + * the actual value of the OBJECT IDENTIFIER. + * + * Returns: %ASN1_SUCCESS if Substitution OK, %ASN1_ERROR_TYPE_ANY if + * some "ANY DEFINED BY" element couldn't be expanded due to a + * problem in OBJECT_ID -> TYPE association, or other error codes + * depending on DER decoding. + **/ +int +asn1_expand_any_defined_by (asn1_node_const definitions, asn1_node * element) +{ + char name[2 * ASN1_MAX_NAME_SIZE + 2], value[ASN1_MAX_NAME_SIZE]; + int retCode = ASN1_SUCCESS, result; + int len, len2, len3; + asn1_node_const p2; + asn1_node p, p3, aux = NULL; + char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + const char *definitionsName; + + if ((definitions == NULL) || (*element == NULL)) + return ASN1_ELEMENT_NOT_FOUND; + + definitionsName = definitions->name; + + p = *element; + while (p) + { + + switch (type_field (p->type)) + { + case ASN1_ETYPE_ANY: + if ((p->type & CONST_DEFINED_BY) && (p->value)) + { + /* search the "DEF_BY" element */ + p2 = p->down; + while ((p2) && (type_field (p2->type) != ASN1_ETYPE_CONSTANT)) + p2 = p2->right; + + if (!p2) + { + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + + p3 = _asn1_find_up (p); + + if (!p3) + { + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + + p3 = p3->down; + while (p3) + { + if (!(strcmp (p3->name, p2->name))) + break; + p3 = p3->right; + } + + if ((!p3) || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) || + (p3->value == NULL)) + { + + p3 = _asn1_find_up (p); + p3 = _asn1_find_up (p3); + + if (!p3) + { + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + + p3 = p3->down; + + while (p3) + { + if (!(strcmp (p3->name, p2->name))) + break; + p3 = p3->right; + } + + if ((!p3) || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) + || (p3->value == NULL)) + { + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + } + + /* search the OBJECT_ID into definitions */ + p2 = definitions->down; + while (p2) + { + if ((type_field (p2->type) == ASN1_ETYPE_OBJECT_ID) && + (p2->type & CONST_ASSIGN)) + { + snprintf (name, sizeof (name), "%s.%s", definitionsName, + p2->name); + + len = ASN1_MAX_NAME_SIZE; + result = + asn1_read_value (definitions, name, value, &len); + + if ((result == ASN1_SUCCESS) + && (!_asn1_strcmp (p3->value, value))) + { + p2 = p2->right; /* pointer to the structure to + use for expansion */ + while ((p2) && (p2->type & CONST_ASSIGN)) + p2 = p2->right; + + if (p2) + { + snprintf (name, sizeof (name), "%s.%s", + definitionsName, p2->name); + + result = + asn1_create_element (definitions, name, &aux); + if (result == ASN1_SUCCESS) + { + _asn1_cpy_name (aux, p); + len2 = + asn1_get_length_der (p->value, + p->value_len, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + + result = + asn1_der_decoding (&aux, p->value + len3, + len2, + errorDescription); + if (result == ASN1_SUCCESS) + { + + _asn1_set_right (aux, p->right); + _asn1_set_right (p, aux); + + result = asn1_delete_structure (&p); + if (result == ASN1_SUCCESS) + { + p = aux; + aux = NULL; + break; + } + else + { /* error with asn1_delete_structure */ + asn1_delete_structure (&aux); + retCode = result; + break; + } + } + else + { /* error with asn1_der_decoding */ + retCode = result; + break; + } + } + else + { /* error with asn1_create_element */ + retCode = result; + break; + } + } + else + { /* error with the pointer to the structure to exapand */ + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + } + } + p2 = p2->right; + } /* end while */ + + if (!p2) + { + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + + } + break; + default: + break; + } + + + if (p->down) + { + p = p->down; + } + else if (p == *element) + { + p = NULL; + break; + } + else if (p->right) + p = p->right; + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == *element) + { + p = NULL; + break; + } + if (p->right) + { + p = p->right; + break; + } + } + } + } + + return retCode; +} + +/** + * asn1_expand_octet_string: + * @definitions: ASN1 definitions + * @element: pointer to an ASN1 structure + * @octetName: name of the OCTECT STRING field to expand. + * @objectName: name of the OBJECT IDENTIFIER field to use to define + * the type for expansion. + * + * Expands an "OCTET STRING" element of a structure created from a DER + * decoding process (the asn1_der_decoding() function). The type used + * for expansion is the first one following the definition of the + * actual value of the OBJECT IDENTIFIER indicated by OBJECTNAME. + * + * Returns: %ASN1_SUCCESS if substitution OK, %ASN1_ELEMENT_NOT_FOUND + * if @objectName or @octetName are not correct, + * %ASN1_VALUE_NOT_VALID if it wasn't possible to find the type to + * use for expansion, or other errors depending on DER decoding. + **/ +int +asn1_expand_octet_string (asn1_node_const definitions, asn1_node * element, + const char *octetName, const char *objectName) +{ + char name[2 * ASN1_MAX_NAME_SIZE + 1], value[ASN1_MAX_NAME_SIZE]; + int retCode = ASN1_SUCCESS, result; + int len, len2, len3; + asn1_node_const p2; + asn1_node aux = NULL; + asn1_node octetNode = NULL, objectNode = NULL; + char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + + if ((definitions == NULL) || (*element == NULL)) + return ASN1_ELEMENT_NOT_FOUND; + + octetNode = asn1_find_node (*element, octetName); + if (octetNode == NULL) + return ASN1_ELEMENT_NOT_FOUND; + if (type_field (octetNode->type) != ASN1_ETYPE_OCTET_STRING) + return ASN1_ELEMENT_NOT_FOUND; + if (octetNode->value == NULL) + return ASN1_VALUE_NOT_FOUND; + + objectNode = asn1_find_node (*element, objectName); + if (objectNode == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + if (type_field (objectNode->type) != ASN1_ETYPE_OBJECT_ID) + return ASN1_ELEMENT_NOT_FOUND; + + if (objectNode->value == NULL) + return ASN1_VALUE_NOT_FOUND; + + + /* search the OBJECT_ID into definitions */ + p2 = definitions->down; + while (p2) + { + if ((type_field (p2->type) == ASN1_ETYPE_OBJECT_ID) && + (p2->type & CONST_ASSIGN)) + { + strcpy (name, definitions->name); + strcat (name, "."); + strcat (name, p2->name); + + len = sizeof (value); + result = asn1_read_value (definitions, name, value, &len); + + if ((result == ASN1_SUCCESS) + && (!_asn1_strcmp (objectNode->value, value))) + { + + p2 = p2->right; /* pointer to the structure to + use for expansion */ + while ((p2) && (p2->type & CONST_ASSIGN)) + p2 = p2->right; + + if (p2) + { + strcpy (name, definitions->name); + strcat (name, "."); + strcat (name, p2->name); + + result = asn1_create_element (definitions, name, &aux); + if (result == ASN1_SUCCESS) + { + _asn1_cpy_name (aux, octetNode); + len2 = + asn1_get_length_der (octetNode->value, + octetNode->value_len, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + + result = + asn1_der_decoding (&aux, octetNode->value + len3, + len2, errorDescription); + if (result == ASN1_SUCCESS) + { + + _asn1_set_right (aux, octetNode->right); + _asn1_set_right (octetNode, aux); + + result = asn1_delete_structure (&octetNode); + if (result == ASN1_SUCCESS) + { + aux = NULL; + break; + } + else + { /* error with asn1_delete_structure */ + asn1_delete_structure (&aux); + retCode = result; + break; + } + } + else + { /* error with asn1_der_decoding */ + retCode = result; + break; + } + } + else + { /* error with asn1_create_element */ + retCode = result; + break; + } + } + else + { /* error with the pointer to the structure to exapand */ + retCode = ASN1_VALUE_NOT_VALID; + break; + } + } + } + + p2 = p2->right; + + } + + if (!p2) + retCode = ASN1_VALUE_NOT_VALID; + + return retCode; +} + +/*- + * _asn1_decode_simple_der: + * @etype: The type of the string to be encoded (ASN1_ETYPE_) + * @der: the encoded string + * @_der_len: the bytes of the encoded string + * @str: a pointer to the data + * @str_len: the length of the data + * @dflags: DECODE_FLAG_* + * + * Decodes a simple DER encoded type (e.g. a string, which is not constructed). + * The output is a pointer inside the @der. + * + * Returns: %ASN1_SUCCESS if successful or an error value. + -*/ +static int +_asn1_decode_simple_der (unsigned int etype, const unsigned char *der, + unsigned int _der_len, const unsigned char **str, + unsigned int *str_len, unsigned dflags) +{ + int tag_len, len_len; + const unsigned char *p; + int der_len = _der_len; + unsigned char class; + unsigned long tag; + long ret; + + if (der == NULL || der_len == 0) + return ASN1_VALUE_NOT_VALID; + + if (ETYPE_OK (etype) == 0 || ETYPE_IS_STRING (etype) == 0) + return ASN1_VALUE_NOT_VALID; + + /* doesn't handle constructed classes */ + class = ETYPE_CLASS (etype); + if (class != ASN1_CLASS_UNIVERSAL) + return ASN1_VALUE_NOT_VALID; + + p = der; + + if (dflags & DECODE_FLAG_HAVE_TAG) + { + ret = asn1_get_tag_der (p, der_len, &class, &tag_len, &tag); + if (ret != ASN1_SUCCESS) + return ret; + + if (class != ETYPE_CLASS (etype) || tag != ETYPE_TAG (etype)) + { + warn (); + return ASN1_DER_ERROR; + } + + p += tag_len; + der_len -= tag_len; + if (der_len <= 0) + return ASN1_DER_ERROR; + } + + ret = asn1_get_length_der (p, der_len, &len_len); + if (ret < 0) + return ASN1_DER_ERROR; + + p += len_len; + der_len -= len_len; + if (der_len <= 0) + return ASN1_DER_ERROR; + + *str_len = ret; + *str = p; + + return ASN1_SUCCESS; +} + +/** + * asn1_decode_simple_der: + * @etype: The type of the string to be encoded (ASN1_ETYPE_) + * @der: the encoded string + * @_der_len: the bytes of the encoded string + * @str: a pointer to the data + * @str_len: the length of the data + * + * Decodes a simple DER encoded type (e.g. a string, which is not constructed). + * The output is a pointer inside the @der. + * + * Returns: %ASN1_SUCCESS if successful or an error value. + **/ +int +asn1_decode_simple_der (unsigned int etype, const unsigned char *der, + unsigned int _der_len, const unsigned char **str, + unsigned int *str_len) +{ + return _asn1_decode_simple_der (etype, der, _der_len, str, str_len, + DECODE_FLAG_HAVE_TAG); +} + +static int +append (uint8_t ** dst, unsigned *dst_size, const unsigned char *src, + unsigned src_size) +{ + if (src_size == 0) + return ASN1_SUCCESS; + + *dst = _asn1_realloc (*dst, *dst_size + src_size); + if (*dst == NULL) + return ASN1_MEM_ALLOC_ERROR; + memcpy (*dst + *dst_size, src, src_size); + *dst_size += src_size; + return ASN1_SUCCESS; +} + +/*- + * _asn1_decode_simple_ber: + * @etype: The type of the string to be encoded (ASN1_ETYPE_) + * @der: the encoded string + * @_der_len: the bytes of the encoded string + * @str: a pointer to the data + * @str_len: the length of the data + * @ber_len: the total length occupied by BER (may be %NULL) + * @have_tag: whether a DER tag is included + * + * Decodes a BER encoded type. The output is an allocated value + * of the data. This decodes BER STRINGS only. Other types are + * decoded as DER. + * + * Returns: %ASN1_SUCCESS if successful or an error value. + -*/ +static int +_asn1_decode_simple_ber (unsigned int etype, const unsigned char *der, + unsigned int _der_len, unsigned char **str, + unsigned int *str_len, unsigned int *ber_len, + unsigned dflags) +{ + int tag_len, len_len; + const unsigned char *p; + int der_len = _der_len; + uint8_t *total = NULL; + unsigned total_size = 0; + unsigned char class; + unsigned long tag; + unsigned char *out = NULL; + const unsigned char *cout = NULL; + unsigned out_len; + long result; + + if (ber_len) + *ber_len = 0; + + if (der == NULL || der_len == 0) + { + warn (); + return ASN1_VALUE_NOT_VALID; + } + + if (ETYPE_OK (etype) == 0) + { + warn (); + return ASN1_VALUE_NOT_VALID; + } + + /* doesn't handle constructed + definite classes */ + class = ETYPE_CLASS (etype); + if (class != ASN1_CLASS_UNIVERSAL) + { + warn (); + return ASN1_VALUE_NOT_VALID; + } + + p = der; + + if (dflags & DECODE_FLAG_HAVE_TAG) + { + result = asn1_get_tag_der (p, der_len, &class, &tag_len, &tag); + if (result != ASN1_SUCCESS) + { + warn (); + return result; + } + + if (tag != ETYPE_TAG (etype)) + { + warn (); + return ASN1_DER_ERROR; + } + + p += tag_len; + + DECR_LEN (der_len, tag_len); + + if (ber_len) + *ber_len += tag_len; + } + + /* indefinite constructed */ + if ((((dflags & DECODE_FLAG_CONSTRUCTED) || class == ASN1_CLASS_STRUCTURED) + && ETYPE_IS_STRING (etype)) && !(dflags & DECODE_FLAG_LEVEL3)) + { + if (der_len == 0) + { + warn (); + result = ASN1_DER_ERROR; + goto cleanup; + } + + if (der_len > 0 && p[0] == 0x80) /* indefinite */ + { + len_len = 1; + DECR_LEN (der_len, len_len); + p += len_len; + + if (ber_len) + *ber_len += len_len; + + /* decode the available octet strings */ + do + { + unsigned tmp_len; + unsigned flags = DECODE_FLAG_HAVE_TAG; + + if (dflags & DECODE_FLAG_LEVEL1) + flags |= DECODE_FLAG_LEVEL2; + else if (dflags & DECODE_FLAG_LEVEL2) + flags |= DECODE_FLAG_LEVEL3; + else + flags |= DECODE_FLAG_LEVEL1; + + result = + _asn1_decode_simple_ber (etype, p, der_len, &out, &out_len, + &tmp_len, flags); + if (result != ASN1_SUCCESS) + { + warn (); + goto cleanup; + } + + p += tmp_len; + DECR_LEN (der_len, tmp_len); + + if (ber_len) + *ber_len += tmp_len; + + DECR_LEN (der_len, 2); /* we need the EOC */ + + result = append (&total, &total_size, out, out_len); + if (result != ASN1_SUCCESS) + { + warn (); + goto cleanup; + } + + free (out); + out = NULL; + + if (p[0] == 0 && p[1] == 0) /* EOC */ + { + if (ber_len) + *ber_len += 2; + break; + } + + /* no EOC */ + der_len += 2; + + if (der_len == 2) + { + warn (); + result = ASN1_DER_ERROR; + goto cleanup; + } + } + while (1); + } + else /* constructed */ + { + long const_len; + + result = asn1_get_length_ber (p, der_len, &len_len); + if (result < 0) + { + warn (); + result = ASN1_DER_ERROR; + goto cleanup; + } + + DECR_LEN (der_len, len_len); + p += len_len; + + const_len = result; + + if (ber_len) + *ber_len += len_len; + + /* decode the available octet strings */ + while (const_len > 0) + { + unsigned tmp_len; + unsigned flags = DECODE_FLAG_HAVE_TAG; + + if (dflags & DECODE_FLAG_LEVEL1) + flags |= DECODE_FLAG_LEVEL2; + else if (dflags & DECODE_FLAG_LEVEL2) + flags |= DECODE_FLAG_LEVEL3; + else + flags |= DECODE_FLAG_LEVEL1; + + result = + _asn1_decode_simple_ber (etype, p, der_len, &out, &out_len, + &tmp_len, flags); + if (result != ASN1_SUCCESS) + { + warn (); + goto cleanup; + } + + p += tmp_len; + DECR_LEN (der_len, tmp_len); + DECR_LEN (const_len, tmp_len); + + if (ber_len) + *ber_len += tmp_len; + + result = append (&total, &total_size, out, out_len); + if (result != ASN1_SUCCESS) + { + warn (); + goto cleanup; + } + + free (out); + out = NULL; + } + } + } + else if (class == ETYPE_CLASS (etype)) + { + if (ber_len) + { + result = asn1_get_length_der (p, der_len, &len_len); + if (result < 0) + { + warn (); + result = ASN1_DER_ERROR; + goto cleanup; + } + *ber_len += result + len_len; + } + + /* non-string values are decoded as DER */ + result = + _asn1_decode_simple_der (etype, der, _der_len, &cout, &out_len, + dflags); + if (result != ASN1_SUCCESS) + { + warn (); + goto cleanup; + } + + result = append (&total, &total_size, cout, out_len); + if (result != ASN1_SUCCESS) + { + warn (); + goto cleanup; + } + } + else + { + warn (); + result = ASN1_DER_ERROR; + goto cleanup; + } + + *str = total; + *str_len = total_size; + + return ASN1_SUCCESS; +cleanup: + free (out); + free (total); + return result; +} + +/** + * asn1_decode_simple_ber: + * @etype: The type of the string to be encoded (ASN1_ETYPE_) + * @der: the encoded string + * @_der_len: the bytes of the encoded string + * @str: a pointer to the data + * @str_len: the length of the data + * @ber_len: the total length occupied by BER (may be %NULL) + * + * Decodes a BER encoded type. The output is an allocated value + * of the data. This decodes BER STRINGS only. Other types are + * decoded as DER. + * + * Returns: %ASN1_SUCCESS if successful or an error value. + **/ +int +asn1_decode_simple_ber (unsigned int etype, const unsigned char *der, + unsigned int _der_len, unsigned char **str, + unsigned int *str_len, unsigned int *ber_len) +{ + return _asn1_decode_simple_ber (etype, der, _der_len, str, str_len, ber_len, + DECODE_FLAG_HAVE_TAG); +} diff --git a/grub-core/lib/libtasn1/lib/element.c b/grub-core/lib/libtasn1/lib/element.c new file mode 100644 index 000000000..d4c558e10 --- /dev/null +++ b/grub-core/lib/libtasn1/lib/element.c @@ -0,0 +1,1109 @@ +/* + * Copyright (C) 2000-2022 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/*****************************************************/ +/* File: element.c */ +/* Description: Functions with the read and write */ +/* functions. */ +/*****************************************************/ + + +#include +#include "parser_aux.h" +#include +#include "structure.h" +#include "c-ctype.h" +#include "element.h" + +void +_asn1_hierarchical_name (asn1_node_const node, char *name, int name_size) +{ + asn1_node_const p; + char tmp_name[64]; + + p = node; + + name[0] = 0; + + while (p != NULL) + { + if (p->name[0] != 0) + { + _asn1_str_cpy (tmp_name, sizeof (tmp_name), name), + _asn1_str_cpy (name, name_size, p->name); + _asn1_str_cat (name, name_size, "."); + _asn1_str_cat (name, name_size, tmp_name); + } + p = _asn1_find_up (p); + } + + if (name[0] == 0) + _asn1_str_cpy (name, name_size, "ROOT"); +} + + +/******************************************************************/ +/* Function : _asn1_convert_integer */ +/* Description: converts an integer from a null terminated string */ +/* to der decoding. The convertion from a null */ +/* terminated string to an integer is made with */ +/* the 'strtol' function. */ +/* Parameters: */ +/* value: null terminated string to convert. */ +/* value_out: convertion result (memory must be already */ +/* allocated). */ +/* value_out_size: number of bytes of value_out. */ +/* len: number of significant byte of value_out. */ +/* Return: ASN1_MEM_ERROR or ASN1_SUCCESS */ +/******************************************************************/ +int +_asn1_convert_integer (const unsigned char *value, unsigned char *value_out, + int value_out_size, int *len) +{ + char negative; + unsigned char val[SIZEOF_UNSIGNED_LONG_INT]; + long valtmp; + int k, k2; + + valtmp = _asn1_strtol (value, NULL, 10); + + for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT; k++) + { + val[SIZEOF_UNSIGNED_LONG_INT - k - 1] = (valtmp >> (8 * k)) & 0xFF; + } + + if (val[0] & 0x80) + negative = 1; + else + negative = 0; + + for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT - 1; k++) + { + if (negative && (val[k] != 0xFF)) + break; + else if (!negative && val[k]) + break; + } + + if ((negative && !(val[k] & 0x80)) || (!negative && (val[k] & 0x80))) + k--; + + *len = SIZEOF_UNSIGNED_LONG_INT - k; + + if (SIZEOF_UNSIGNED_LONG_INT - k > value_out_size) + /* VALUE_OUT is too short to contain the value conversion */ + return ASN1_MEM_ERROR; + + if (value_out != NULL) + { + for (k2 = k; k2 < SIZEOF_UNSIGNED_LONG_INT; k2++) + value_out[k2 - k] = val[k2]; + } + +#if 0 + printf ("_asn1_convert_integer: valueIn=%s, lenOut=%d", value, *len); + for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT; k++) + printf (", vOut[%d]=%d", k, value_out[k]); + printf ("\n"); +#endif + + return ASN1_SUCCESS; +} + +/* Appends a new element into the sequence (or set) defined by this + * node. The new element will have a name of '?number', where number + * is a monotonically increased serial number. + * + * The last element in the list may be provided in @pcache, to avoid + * traversing the list, an expensive operation in long lists. + * + * On success it returns in @pcache the added element (which is the + * tail in the list of added elements). + */ +int +_asn1_append_sequence_set (asn1_node node, struct node_tail_cache_st *pcache) +{ + asn1_node p, p2; + char temp[LTOSTR_MAX_SIZE + 1]; + long n; + + if (!node || !(node->down)) + return ASN1_GENERIC_ERROR; + + p = node->down; + while ((type_field (p->type) == ASN1_ETYPE_TAG) + || (type_field (p->type) == ASN1_ETYPE_SIZE)) + p = p->right; + + p2 = _asn1_copy_structure3 (p); + if (p2 == NULL) + return ASN1_GENERIC_ERROR; + + if (pcache == NULL || pcache->tail == NULL || pcache->head != node) + { + while (p->right) + { + p = p->right; + } + } + else + { + p = pcache->tail; + } + + _asn1_set_right (p, p2); + if (pcache) + { + pcache->head = node; + pcache->tail = p2; + } + + if (p->name[0] == 0) + _asn1_str_cpy (temp, sizeof (temp), "?1"); + else + { + n = strtol (p->name + 1, NULL, 0); + n++; + temp[0] = '?'; + _asn1_ltostr (n, temp + 1); + } + _asn1_set_name (p2, temp); + /* p2->type |= CONST_OPTION; */ + + return ASN1_SUCCESS; +} + + +/** + * asn1_write_value: + * @node_root: pointer to a structure + * @name: the name of the element inside the structure that you want to set. + * @ivalue: vector used to specify the value to set. If len is >0, + * VALUE must be a two's complement form integer. if len=0 *VALUE + * must be a null terminated string with an integer value. + * @len: number of bytes of *value to use to set the value: + * value[0]..value[len-1] or 0 if value is a null terminated string + * + * Set the value of one element inside a structure. + * + * If an element is OPTIONAL and you want to delete it, you must use + * the value=NULL and len=0. Using "pkix.asn": + * + * result=asn1_write_value(cert, "tbsCertificate.issuerUniqueID", + * NULL, 0); + * + * Description for each type: + * + * INTEGER: VALUE must contain a two's complement form integer. + * + * value[0]=0xFF , len=1 -> integer=-1. + * value[0]=0xFF value[1]=0xFF , len=2 -> integer=-1. + * value[0]=0x01 , len=1 -> integer= 1. + * value[0]=0x00 value[1]=0x01 , len=2 -> integer= 1. + * value="123" , len=0 -> integer= 123. + * + * ENUMERATED: As INTEGER (but only with not negative numbers). + * + * BOOLEAN: VALUE must be the null terminated string "TRUE" or + * "FALSE" and LEN != 0. + * + * value="TRUE" , len=1 -> boolean=TRUE. + * value="FALSE" , len=1 -> boolean=FALSE. + * + * OBJECT IDENTIFIER: VALUE must be a null terminated string with + * each number separated by a dot (e.g. "1.2.3.543.1"). LEN != 0. + * + * value="1 2 840 10040 4 3" , len=1 -> OID=dsa-with-sha. + * + * UTCTime: VALUE must be a null terminated string in one of these + * formats: "YYMMDDhhmmssZ", "YYMMDDhhmmssZ", + * "YYMMDDhhmmss+hh'mm'", "YYMMDDhhmmss-hh'mm'", + * "YYMMDDhhmm+hh'mm'", or "YYMMDDhhmm-hh'mm'". LEN != 0. + * + * value="9801011200Z" , len=1 -> time=Jannuary 1st, 1998 + * at 12h 00m Greenwich Mean Time + * + * GeneralizedTime: VALUE must be in one of this format: + * "YYYYMMDDhhmmss.sZ", "YYYYMMDDhhmmss.sZ", + * "YYYYMMDDhhmmss.s+hh'mm'", "YYYYMMDDhhmmss.s-hh'mm'", + * "YYYYMMDDhhmm+hh'mm'", or "YYYYMMDDhhmm-hh'mm'" where ss.s + * indicates the seconds with any precision like "10.1" or "01.02". + * LEN != 0 + * + * value="2001010112001.12-0700" , len=1 -> time=Jannuary + * 1st, 2001 at 12h 00m 01.12s Pacific Daylight Time + * + * OCTET STRING: VALUE contains the octet string and LEN is the + * number of octets. + * + * value="$\backslash$x01$\backslash$x02$\backslash$x03" , + * len=3 -> three bytes octet string + * + * GeneralString: VALUE contains the generalstring and LEN is the + * number of octets. + * + * value="$\backslash$x01$\backslash$x02$\backslash$x03" , + * len=3 -> three bytes generalstring + * + * BIT STRING: VALUE contains the bit string organized by bytes and + * LEN is the number of bits. + * + * value="$\backslash$xCF" , len=6 -> bit string="110011" (six + * bits) + * + * CHOICE: if NAME indicates a choice type, VALUE must specify one of + * the alternatives with a null terminated string. LEN != 0. Using + * "pkix.asn"\: + * + * result=asn1_write_value(cert, + * "certificate1.tbsCertificate.subject", "rdnSequence", + * 1); + * + * ANY: VALUE indicates the der encoding of a structure. LEN != 0. + * + * SEQUENCE OF: VALUE must be the null terminated string "NEW" and + * LEN != 0. With this instruction another element is appended in + * the sequence. The name of this element will be "?1" if it's the + * first one, "?2" for the second and so on. + * + * Using "pkix.asn"\: + * + * result=asn1_write_value(cert, + * "certificate1.tbsCertificate.subject.rdnSequence", "NEW", 1); + * + * SET OF: the same as SEQUENCE OF. Using "pkix.asn": + * + * result=asn1_write_value(cert, + * "tbsCertificate.subject.rdnSequence.?LAST", "NEW", 1); + * + * Returns: %ASN1_SUCCESS if the value was set, + * %ASN1_ELEMENT_NOT_FOUND if @name is not a valid element, and + * %ASN1_VALUE_NOT_VALID if @ivalue has a wrong format. + **/ +int +asn1_write_value (asn1_node node_root, const char *name, + const void *ivalue, int len) +{ + asn1_node node, p, p2; + unsigned char *temp, *value_temp = NULL, *default_temp = NULL; + int len2, k, k2, negative; + size_t i; + const unsigned char *value = ivalue; + unsigned int type; + + node = asn1_find_node (node_root, name); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + if ((node->type & CONST_OPTION) && (value == NULL) && (len == 0)) + { + asn1_delete_structure (&node); + return ASN1_SUCCESS; + } + + type = type_field (node->type); + + if ((type == ASN1_ETYPE_SEQUENCE_OF || type == ASN1_ETYPE_SET_OF) + && (value == NULL) && (len == 0)) + { + p = node->down; + while ((type_field (p->type) == ASN1_ETYPE_TAG) + || (type_field (p->type) == ASN1_ETYPE_SIZE)) + p = p->right; + + while (p->right) + asn1_delete_structure (&p->right); + + return ASN1_SUCCESS; + } + + /* Don't allow element deletion for other types */ + if (value == NULL) + { + return ASN1_VALUE_NOT_VALID; + } + + switch (type) + { + case ASN1_ETYPE_BOOLEAN: + if (!_asn1_strcmp (value, "TRUE")) + { + if (node->type & CONST_DEFAULT) + { + p = node->down; + while (type_field (p->type) != ASN1_ETYPE_DEFAULT) + p = p->right; + if (p->type & CONST_TRUE) + _asn1_set_value (node, NULL, 0); + else + _asn1_set_value (node, "T", 1); + } + else + _asn1_set_value (node, "T", 1); + } + else if (!_asn1_strcmp (value, "FALSE")) + { + if (node->type & CONST_DEFAULT) + { + p = node->down; + while (type_field (p->type) != ASN1_ETYPE_DEFAULT) + p = p->right; + if (p->type & CONST_FALSE) + _asn1_set_value (node, NULL, 0); + else + _asn1_set_value (node, "F", 1); + } + else + _asn1_set_value (node, "F", 1); + } + else + return ASN1_VALUE_NOT_VALID; + break; + case ASN1_ETYPE_INTEGER: + case ASN1_ETYPE_ENUMERATED: + if (len == 0) + { + if ((c_isdigit (value[0])) || (value[0] == '-')) + { + value_temp = malloc (SIZEOF_UNSIGNED_LONG_INT); + if (value_temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + _asn1_convert_integer (value, value_temp, + SIZEOF_UNSIGNED_LONG_INT, &len); + } + else + { /* is an identifier like v1 */ + if (!(node->type & CONST_LIST)) + return ASN1_VALUE_NOT_VALID; + p = node->down; + while (p) + { + if (type_field (p->type) == ASN1_ETYPE_CONSTANT) + { + if (!_asn1_strcmp (p->name, value)) + { + value_temp = malloc (SIZEOF_UNSIGNED_LONG_INT); + if (value_temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + _asn1_convert_integer (p->value, + value_temp, + SIZEOF_UNSIGNED_LONG_INT, + &len); + break; + } + } + p = p->right; + } + if (p == NULL) + return ASN1_VALUE_NOT_VALID; + } + } + else + { /* len != 0 */ + value_temp = malloc (len); + if (value_temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + memcpy (value_temp, value, len); + } + + if (value_temp[0] & 0x80) + negative = 1; + else + negative = 0; + + if (negative && (type_field (node->type) == ASN1_ETYPE_ENUMERATED)) + { + free (value_temp); + return ASN1_VALUE_NOT_VALID; + } + + for (k = 0; k < len - 1; k++) + if (negative && (value_temp[k] != 0xFF)) + break; + else if (!negative && value_temp[k]) + break; + + if ((negative && !(value_temp[k] & 0x80)) || + (!negative && (value_temp[k] & 0x80))) + k--; + + _asn1_set_value_lv (node, value_temp + k, len - k); + + if (node->type & CONST_DEFAULT) + { + p = node->down; + while (type_field (p->type) != ASN1_ETYPE_DEFAULT) + p = p->right; + if ((c_isdigit (p->value[0])) || (p->value[0] == '-')) + { + default_temp = malloc (SIZEOF_UNSIGNED_LONG_INT); + if (default_temp == NULL) + { + free (value_temp); + return ASN1_MEM_ALLOC_ERROR; + } + + _asn1_convert_integer (p->value, default_temp, + SIZEOF_UNSIGNED_LONG_INT, &len2); + } + else + { /* is an identifier like v1 */ + if (!(node->type & CONST_LIST)) + { + free (value_temp); + return ASN1_VALUE_NOT_VALID; + } + p2 = node->down; + while (p2) + { + if (type_field (p2->type) == ASN1_ETYPE_CONSTANT) + { + if (!_asn1_strcmp (p2->name, p->value)) + { + default_temp = malloc (SIZEOF_UNSIGNED_LONG_INT); + if (default_temp == NULL) + { + free (value_temp); + return ASN1_MEM_ALLOC_ERROR; + } + + _asn1_convert_integer (p2->value, + default_temp, + SIZEOF_UNSIGNED_LONG_INT, + &len2); + break; + } + } + p2 = p2->right; + } + if (p2 == NULL) + { + free (value_temp); + return ASN1_VALUE_NOT_VALID; + } + } + + + if ((len - k) == len2) + { + for (k2 = 0; k2 < len2; k2++) + if (value_temp[k + k2] != default_temp[k2]) + { + break; + } + if (k2 == len2) + _asn1_set_value (node, NULL, 0); + } + free (default_temp); + } + free (value_temp); + break; + case ASN1_ETYPE_OBJECT_ID: + for (i = 0; i < _asn1_strlen (value); i++) + if ((!c_isdigit (value[i])) && (value[i] != '.') && (value[i] != '+')) + return ASN1_VALUE_NOT_VALID; + if (node->type & CONST_DEFAULT) + { + p = node->down; + while (type_field (p->type) != ASN1_ETYPE_DEFAULT) + p = p->right; + if (!_asn1_strcmp (value, p->value)) + { + _asn1_set_value (node, NULL, 0); + break; + } + } + _asn1_set_value (node, value, _asn1_strlen (value) + 1); + break; + case ASN1_ETYPE_UTC_TIME: + { + len = _asn1_strlen (value); + if (len < 11) + return ASN1_VALUE_NOT_VALID; + for (k = 0; k < 10; k++) + if (!c_isdigit (value[k])) + return ASN1_VALUE_NOT_VALID; + switch (len) + { + case 11: + if (value[10] != 'Z') + return ASN1_VALUE_NOT_VALID; + break; + case 13: + if ((!c_isdigit (value[10])) || (!c_isdigit (value[11])) || + (value[12] != 'Z')) + return ASN1_VALUE_NOT_VALID; + break; + case 15: + if ((value[10] != '+') && (value[10] != '-')) + return ASN1_VALUE_NOT_VALID; + for (k = 11; k < 15; k++) + if (!c_isdigit (value[k])) + return ASN1_VALUE_NOT_VALID; + break; + case 17: + if ((!c_isdigit (value[10])) || (!c_isdigit (value[11]))) + return ASN1_VALUE_NOT_VALID; + if ((value[12] != '+') && (value[12] != '-')) + return ASN1_VALUE_NOT_VALID; + for (k = 13; k < 17; k++) + if (!c_isdigit (value[k])) + return ASN1_VALUE_NOT_VALID; + break; + default: + return ASN1_VALUE_NOT_FOUND; + } + _asn1_set_value (node, value, len); + } + break; + case ASN1_ETYPE_GENERALIZED_TIME: + len = _asn1_strlen (value); + _asn1_set_value (node, value, len); + break; + case ASN1_ETYPE_OCTET_STRING: + case ASN1_ETYPE_GENERALSTRING: + case ASN1_ETYPE_NUMERIC_STRING: + case ASN1_ETYPE_IA5_STRING: + case ASN1_ETYPE_TELETEX_STRING: + case ASN1_ETYPE_PRINTABLE_STRING: + case ASN1_ETYPE_UNIVERSAL_STRING: + case ASN1_ETYPE_BMP_STRING: + case ASN1_ETYPE_UTF8_STRING: + case ASN1_ETYPE_VISIBLE_STRING: + if (len == 0) + len = _asn1_strlen (value); + _asn1_set_value_lv (node, value, len); + break; + case ASN1_ETYPE_BIT_STRING: + if (len == 0) + len = _asn1_strlen (value); + asn1_length_der ((len >> 3) + 2, NULL, &len2); + temp = malloc ((len >> 3) + 2 + len2); + if (temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + asn1_bit_der (value, len, temp, &len2); + _asn1_set_value_m (node, temp, len2); + temp = NULL; + break; + case ASN1_ETYPE_CHOICE: + p = node->down; + while (p) + { + if (!_asn1_strcmp (p->name, value)) + { + p2 = node->down; + while (p2) + { + if (p2 != p) + { + asn1_delete_structure (&p2); + p2 = node->down; + } + else + p2 = p2->right; + } + break; + } + p = p->right; + } + if (!p) + return ASN1_ELEMENT_NOT_FOUND; + break; + case ASN1_ETYPE_ANY: + _asn1_set_value_lv (node, value, len); + break; + case ASN1_ETYPE_SEQUENCE_OF: + case ASN1_ETYPE_SET_OF: + if (_asn1_strcmp (value, "NEW")) + return ASN1_VALUE_NOT_VALID; + _asn1_append_sequence_set (node, NULL); + break; + default: + return ASN1_ELEMENT_NOT_FOUND; + break; + } + + return ASN1_SUCCESS; +} + + +#define PUT_VALUE( ptr, ptr_size, data, data_size) \ + *len = data_size; \ + if (ptr_size < data_size) { \ + return ASN1_MEM_ERROR; \ + } else { \ + if (ptr && data_size > 0) \ + memcpy (ptr, data, data_size); \ + } + +#define PUT_STR_VALUE( ptr, ptr_size, data) \ + *len = _asn1_strlen (data) + 1; \ + if (ptr_size < *len) { \ + return ASN1_MEM_ERROR; \ + } else { \ + /* this strcpy is checked */ \ + if (ptr) { \ + _asn1_strcpy (ptr, data); \ + } \ + } + +#define PUT_AS_STR_VALUE( ptr, ptr_size, data, data_size) \ + *len = data_size + 1; \ + if (ptr_size < *len) { \ + return ASN1_MEM_ERROR; \ + } else { \ + /* this strcpy is checked */ \ + if (ptr) { \ + if (data_size > 0) \ + memcpy (ptr, data, data_size); \ + ptr[data_size] = 0; \ + } \ + } + +#define ADD_STR_VALUE( ptr, ptr_size, data) \ + *len += _asn1_strlen(data); \ + if (ptr_size < (int) *len) { \ + (*len)++; \ + return ASN1_MEM_ERROR; \ + } else { \ + /* this strcat is checked */ \ + if (ptr) _asn1_strcat (ptr, data); \ + } + +/** + * asn1_read_value: + * @root: pointer to a structure. + * @name: the name of the element inside a structure that you want to read. + * @ivalue: vector that will contain the element's content, must be a + * pointer to memory cells already allocated (may be %NULL). + * @len: number of bytes of *value: value[0]..value[len-1]. Initialy + * holds the sizeof value. + * + * Returns the value of one element inside a structure. + * If an element is OPTIONAL and this returns + * %ASN1_ELEMENT_NOT_FOUND, it means that this element wasn't present + * in the der encoding that created the structure. The first element + * of a SEQUENCE_OF or SET_OF is named "?1". The second one "?2" and + * so on. If the @root provided is a node to specific sequence element, + * then the keyword "?CURRENT" is also acceptable and indicates the + * current sequence element of this node. + * + * Note that there can be valid values with length zero. In these case + * this function will succeed and @len will be zero. + * + * INTEGER: VALUE will contain a two's complement form integer. + * + * integer=-1 -> value[0]=0xFF , len=1. + * integer=1 -> value[0]=0x01 , len=1. + * + * ENUMERATED: As INTEGER (but only with not negative numbers). + * + * BOOLEAN: VALUE will be the null terminated string "TRUE" or + * "FALSE" and LEN=5 or LEN=6. + * + * OBJECT IDENTIFIER: VALUE will be a null terminated string with + * each number separated by a dot (i.e. "1.2.3.543.1"). + * + * LEN = strlen(VALUE)+1 + * + * UTCTime: VALUE will be a null terminated string in one of these + * formats: "YYMMDDhhmmss+hh'mm'" or "YYMMDDhhmmss-hh'mm'". + * LEN=strlen(VALUE)+1. + * + * GeneralizedTime: VALUE will be a null terminated string in the + * same format used to set the value. + * + * OCTET STRING: VALUE will contain the octet string and LEN will be + * the number of octets. + * + * GeneralString: VALUE will contain the generalstring and LEN will + * be the number of octets. + * + * BIT STRING: VALUE will contain the bit string organized by bytes + * and LEN will be the number of bits. + * + * CHOICE: If NAME indicates a choice type, VALUE will specify the + * alternative selected. + * + * ANY: If NAME indicates an any type, VALUE will indicate the DER + * encoding of the structure actually used. + * + * Returns: %ASN1_SUCCESS if value is returned, + * %ASN1_ELEMENT_NOT_FOUND if @name is not a valid element, + * %ASN1_VALUE_NOT_FOUND if there isn't any value for the element + * selected, and %ASN1_MEM_ERROR if The value vector isn't big enough + * to store the result, and in this case @len will contain the number of + * bytes needed. On the occasion that the stored data are of zero-length + * this function may return %ASN1_SUCCESS even if the provided @len is zero. + **/ +int +asn1_read_value (asn1_node_const root, const char *name, void *ivalue, + int *len) +{ + return asn1_read_value_type (root, name, ivalue, len, NULL); +} + +/** + * asn1_read_value_type: + * @root: pointer to a structure. + * @name: the name of the element inside a structure that you want to read. + * @ivalue: vector that will contain the element's content, must be a + * pointer to memory cells already allocated (may be %NULL). + * @len: number of bytes of *value: value[0]..value[len-1]. Initialy + * holds the sizeof value. + * @etype: The type of the value read (ASN1_ETYPE) + * + * Returns the type and value of one element inside a structure. + * If an element is OPTIONAL and this returns + * %ASN1_ELEMENT_NOT_FOUND, it means that this element wasn't present + * in the der encoding that created the structure. The first element + * of a SEQUENCE_OF or SET_OF is named "?1". The second one "?2" and + * so on. If the @root provided is a node to specific sequence element, + * then the keyword "?CURRENT" is also acceptable and indicates the + * current sequence element of this node. + * + * Note that there can be valid values with length zero. In these case + * this function will succeed and @len will be zero. + * + * + * INTEGER: VALUE will contain a two's complement form integer. + * + * integer=-1 -> value[0]=0xFF , len=1. + * integer=1 -> value[0]=0x01 , len=1. + * + * ENUMERATED: As INTEGER (but only with not negative numbers). + * + * BOOLEAN: VALUE will be the null terminated string "TRUE" or + * "FALSE" and LEN=5 or LEN=6. + * + * OBJECT IDENTIFIER: VALUE will be a null terminated string with + * each number separated by a dot (i.e. "1.2.3.543.1"). + * + * LEN = strlen(VALUE)+1 + * + * UTCTime: VALUE will be a null terminated string in one of these + * formats: "YYMMDDhhmmss+hh'mm'" or "YYMMDDhhmmss-hh'mm'". + * LEN=strlen(VALUE)+1. + * + * GeneralizedTime: VALUE will be a null terminated string in the + * same format used to set the value. + * + * OCTET STRING: VALUE will contain the octet string and LEN will be + * the number of octets. + * + * GeneralString: VALUE will contain the generalstring and LEN will + * be the number of octets. + * + * BIT STRING: VALUE will contain the bit string organized by bytes + * and LEN will be the number of bits. + * + * CHOICE: If NAME indicates a choice type, VALUE will specify the + * alternative selected. + * + * ANY: If NAME indicates an any type, VALUE will indicate the DER + * encoding of the structure actually used. + * + * Returns: %ASN1_SUCCESS if value is returned, + * %ASN1_ELEMENT_NOT_FOUND if @name is not a valid element, + * %ASN1_VALUE_NOT_FOUND if there isn't any value for the element + * selected, and %ASN1_MEM_ERROR if The value vector isn't big enough + * to store the result, and in this case @len will contain the number of + * bytes needed. On the occasion that the stored data are of zero-length + * this function may return %ASN1_SUCCESS even if the provided @len is zero. + **/ +int +asn1_read_value_type (asn1_node_const root, const char *name, void *ivalue, + int *len, unsigned int *etype) +{ + asn1_node_const node, p, p2; + int len2, len3, result; + int value_size = *len; + unsigned char *value = ivalue; + unsigned type; + + node = asn1_find_node (root, name); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + type = type_field (node->type); + + if ((type != ASN1_ETYPE_NULL) && + (type != ASN1_ETYPE_CHOICE) && + !(node->type & CONST_DEFAULT) && !(node->type & CONST_ASSIGN) && + (node->value == NULL)) + return ASN1_VALUE_NOT_FOUND; + + if (etype) + *etype = type; + switch (type) + { + case ASN1_ETYPE_NULL: + PUT_STR_VALUE (value, value_size, "NULL"); + break; + case ASN1_ETYPE_BOOLEAN: + if ((node->type & CONST_DEFAULT) && (node->value == NULL)) + { + p = node->down; + while (type_field (p->type) != ASN1_ETYPE_DEFAULT) + p = p->right; + if (p->type & CONST_TRUE) + { + PUT_STR_VALUE (value, value_size, "TRUE"); + } + else + { + PUT_STR_VALUE (value, value_size, "FALSE"); + } + } + else if (node->value[0] == 'T') + { + PUT_STR_VALUE (value, value_size, "TRUE"); + } + else + { + PUT_STR_VALUE (value, value_size, "FALSE"); + } + break; + case ASN1_ETYPE_INTEGER: + case ASN1_ETYPE_ENUMERATED: + if ((node->type & CONST_DEFAULT) && (node->value == NULL)) + { + p = node->down; + while (type_field (p->type) != ASN1_ETYPE_DEFAULT) + p = p->right; + if ((c_isdigit (p->value[0])) || (p->value[0] == '-') + || (p->value[0] == '+')) + { + result = _asn1_convert_integer + (p->value, value, value_size, len); + if (result != ASN1_SUCCESS) + return result; + } + else + { /* is an identifier like v1 */ + p2 = node->down; + while (p2) + { + if (type_field (p2->type) == ASN1_ETYPE_CONSTANT) + { + if (!_asn1_strcmp (p2->name, p->value)) + { + result = _asn1_convert_integer + (p2->value, value, value_size, len); + if (result != ASN1_SUCCESS) + return result; + break; + } + } + p2 = p2->right; + } + } + } + else + { + len2 = -1; + result = asn1_get_octet_der + (node->value, node->value_len, &len2, value, value_size, len); + if (result != ASN1_SUCCESS) + return result; + } + break; + case ASN1_ETYPE_OBJECT_ID: + if (node->type & CONST_ASSIGN) + { + *len = 0; + if (value) + value[0] = 0; + p = node->down; + while (p) + { + if (type_field (p->type) == ASN1_ETYPE_CONSTANT) + { + ADD_STR_VALUE (value, value_size, p->value); + if (p->right) + { + ADD_STR_VALUE (value, value_size, "."); + } + } + p = p->right; + } + (*len)++; + } + else if ((node->type & CONST_DEFAULT) && (node->value == NULL)) + { + p = node->down; + while (type_field (p->type) != ASN1_ETYPE_DEFAULT) + p = p->right; + PUT_STR_VALUE (value, value_size, p->value); + } + else + { + PUT_STR_VALUE (value, value_size, node->value); + } + break; + case ASN1_ETYPE_GENERALIZED_TIME: + case ASN1_ETYPE_UTC_TIME: + PUT_AS_STR_VALUE (value, value_size, node->value, node->value_len); + break; + case ASN1_ETYPE_OCTET_STRING: + case ASN1_ETYPE_GENERALSTRING: + case ASN1_ETYPE_NUMERIC_STRING: + case ASN1_ETYPE_IA5_STRING: + case ASN1_ETYPE_TELETEX_STRING: + case ASN1_ETYPE_PRINTABLE_STRING: + case ASN1_ETYPE_UNIVERSAL_STRING: + case ASN1_ETYPE_BMP_STRING: + case ASN1_ETYPE_UTF8_STRING: + case ASN1_ETYPE_VISIBLE_STRING: + len2 = -1; + result = asn1_get_octet_der + (node->value, node->value_len, &len2, value, value_size, len); + if (result != ASN1_SUCCESS) + return result; + break; + case ASN1_ETYPE_BIT_STRING: + len2 = -1; + result = asn1_get_bit_der + (node->value, node->value_len, &len2, value, value_size, len); + if (result != ASN1_SUCCESS) + return result; + break; + case ASN1_ETYPE_CHOICE: + PUT_STR_VALUE (value, value_size, node->down->name); + break; + case ASN1_ETYPE_ANY: + len3 = -1; + len2 = asn1_get_length_der (node->value, node->value_len, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + PUT_VALUE (value, value_size, node->value + len3, len2); + break; + default: + return ASN1_ELEMENT_NOT_FOUND; + break; + } + return ASN1_SUCCESS; +} + + +/** + * asn1_read_tag: + * @root: pointer to a structure + * @name: the name of the element inside a structure. + * @tagValue: variable that will contain the TAG value. + * @classValue: variable that will specify the TAG type. + * + * Returns the TAG and the CLASS of one element inside a structure. + * CLASS can have one of these constants: %ASN1_CLASS_APPLICATION, + * %ASN1_CLASS_UNIVERSAL, %ASN1_CLASS_PRIVATE or + * %ASN1_CLASS_CONTEXT_SPECIFIC. + * + * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if + * @name is not a valid element. + **/ +int +asn1_read_tag (asn1_node_const root, const char *name, int *tagValue, + int *classValue) +{ + asn1_node node, p, pTag; + + node = asn1_find_node (root, name); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node->down; + + /* pTag will points to the IMPLICIT TAG */ + pTag = NULL; + if (node->type & CONST_TAG) + { + while (p) + { + if (type_field (p->type) == ASN1_ETYPE_TAG) + { + if ((p->type & CONST_IMPLICIT) && (pTag == NULL)) + pTag = p; + else if (p->type & CONST_EXPLICIT) + pTag = NULL; + } + p = p->right; + } + } + + if (pTag) + { + *tagValue = _asn1_strtoul (pTag->value, NULL, 10); + + if (pTag->type & CONST_APPLICATION) + *classValue = ASN1_CLASS_APPLICATION; + else if (pTag->type & CONST_UNIVERSAL) + *classValue = ASN1_CLASS_UNIVERSAL; + else if (pTag->type & CONST_PRIVATE) + *classValue = ASN1_CLASS_PRIVATE; + else + *classValue = ASN1_CLASS_CONTEXT_SPECIFIC; + } + else + { + unsigned type = type_field (node->type); + *classValue = ASN1_CLASS_UNIVERSAL; + + switch (type) + { + CASE_HANDLED_ETYPES: + *tagValue = _asn1_tags[type].tag; + break; + case ASN1_ETYPE_TAG: + case ASN1_ETYPE_CHOICE: + case ASN1_ETYPE_ANY: + *tagValue = -1; + break; + default: + break; + } + } + + return ASN1_SUCCESS; +} + +/** + * asn1_read_node_value: + * @node: pointer to a node. + * @data: a point to a asn1_data_node_st + * + * Returns the value a data node inside a asn1_node structure. + * The data returned should be handled as constant values. + * + * Returns: %ASN1_SUCCESS if the node exists. + **/ +int +asn1_read_node_value (asn1_node_const node, asn1_data_node_st * data) +{ + data->name = node->name; + data->value = node->value; + data->value_len = node->value_len; + data->type = type_field (node->type); + + return ASN1_SUCCESS; +} diff --git a/grub-core/lib/libtasn1/lib/element.h b/grub-core/lib/libtasn1/lib/element.h new file mode 100644 index 000000000..8dd0ceba8 --- /dev/null +++ b/grub-core/lib/libtasn1/lib/element.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2000-2022 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _ELEMENT_H +# define _ELEMENT_H + + +struct node_tail_cache_st +{ + asn1_node head; /* the first element of the sequence */ + asn1_node tail; +}; + +int _asn1_append_sequence_set (asn1_node node, + struct node_tail_cache_st *pcached); + +int _asn1_convert_integer (const unsigned char *value, + unsigned char *value_out, + int value_out_size, int *len); + +void _asn1_hierarchical_name (asn1_node_const node, char *name, + int name_size); + +#endif diff --git a/grub-core/lib/libtasn1/lib/errors.c b/grub-core/lib/libtasn1/lib/errors.c new file mode 100644 index 000000000..aef5dfe6f --- /dev/null +++ b/grub-core/lib/libtasn1/lib/errors.c @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2002-2022 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include +#ifdef STDC_HEADERS +# include +#endif + +#define LIBTASN1_ERROR_ENTRY(name) { #name, name } + +struct libtasn1_error_entry +{ + const char *name; + int number; +}; +typedef struct libtasn1_error_entry libtasn1_error_entry; + +static const libtasn1_error_entry error_algorithms[] = { + LIBTASN1_ERROR_ENTRY (ASN1_SUCCESS), + LIBTASN1_ERROR_ENTRY (ASN1_FILE_NOT_FOUND), + LIBTASN1_ERROR_ENTRY (ASN1_ELEMENT_NOT_FOUND), + LIBTASN1_ERROR_ENTRY (ASN1_IDENTIFIER_NOT_FOUND), + LIBTASN1_ERROR_ENTRY (ASN1_DER_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_VALUE_NOT_FOUND), + LIBTASN1_ERROR_ENTRY (ASN1_GENERIC_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_VALUE_NOT_VALID), + LIBTASN1_ERROR_ENTRY (ASN1_TAG_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_TAG_IMPLICIT), + LIBTASN1_ERROR_ENTRY (ASN1_ERROR_TYPE_ANY), + LIBTASN1_ERROR_ENTRY (ASN1_SYNTAX_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_MEM_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_MEM_ALLOC_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_DER_OVERFLOW), + LIBTASN1_ERROR_ENTRY (ASN1_NAME_TOO_LONG), + LIBTASN1_ERROR_ENTRY (ASN1_ARRAY_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_ELEMENT_NOT_EMPTY), + LIBTASN1_ERROR_ENTRY (ASN1_TIME_ENCODING_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_RECURSION), + {0, 0} +}; + +/** + * asn1_perror: + * @error: is an error returned by a libtasn1 function. + * + * Prints a string to stderr with a description of an error. This + * function is like perror(). The only difference is that it accepts + * an error returned by a libtasn1 function. + * + * Since: 1.6 + **/ +void +asn1_perror (int error) +{ + const char *str = asn1_strerror (error); + fprintf (stderr, "LIBTASN1 ERROR: %s\n", str ? str : "(null)"); +} + +/** + * asn1_strerror: + * @error: is an error returned by a libtasn1 function. + * + * Returns a string with a description of an error. This function is + * similar to strerror. The only difference is that it accepts an + * error (number) returned by a libtasn1 function. + * + * Returns: Pointer to static zero-terminated string describing error + * code. + * + * Since: 1.6 + **/ +const char * +asn1_strerror (int error) +{ + const libtasn1_error_entry *p; + + for (p = error_algorithms; p->name != NULL; p++) + if (p->number == error) + return p->name + sizeof ("ASN1_") - 1; + + return NULL; +} diff --git a/grub-core/lib/libtasn1/lib/gstr.c b/grub-core/lib/libtasn1/lib/gstr.c new file mode 100644 index 000000000..eef419554 --- /dev/null +++ b/grub-core/lib/libtasn1/lib/gstr.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2002-2022 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include +#include "gstr.h" + +/* These function are like strcat, strcpy. They only + * do bounds checking (they shouldn't cause buffer overruns), + * and they always produce null terminated strings. + * + * They should be used only with null terminated strings. + */ +void +_asn1_str_cat (char *dest, size_t dest_tot_size, const char *src) +{ + size_t str_size = strlen (src); + size_t dest_size = strlen (dest); + + if (dest_tot_size - dest_size > str_size) + { + strcat (dest, src); + } + else + { + if (dest_tot_size > dest_size) + { + strncat (dest, src, (dest_tot_size - dest_size) - 1); + dest[dest_tot_size - 1] = 0; + } + } +} + +/* Returns the bytes copied (not including the null terminator) */ +unsigned int +_asn1_str_cpy (char *dest, size_t dest_tot_size, const char *src) +{ + size_t str_size = strlen (src); + + if (dest_tot_size > str_size) + { + strcpy (dest, src); + return str_size; + } + else + { + if (dest_tot_size > 0) + { + str_size = dest_tot_size - 1; + memcpy (dest, src, str_size); + dest[str_size] = 0; + return str_size; + } + else + return 0; + } +} diff --git a/grub-core/lib/libtasn1/lib/gstr.h b/grub-core/lib/libtasn1/lib/gstr.h new file mode 100644 index 000000000..99be6c4af --- /dev/null +++ b/grub-core/lib/libtasn1/lib/gstr.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2002-2022 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef GSTR_H +# define GSTR_H + +unsigned int _asn1_str_cpy (char *dest, size_t dest_tot_size, + const char *src); +void _asn1_str_cat (char *dest, size_t dest_tot_size, const char *src); + +# define Estrcpy(x,y) _asn1_str_cpy(x,ASN1_MAX_ERROR_DESCRIPTION_SIZE,y) +# define Estrcat(x,y) _asn1_str_cat(x,ASN1_MAX_ERROR_DESCRIPTION_SIZE,y) + +inline static void +safe_memset (void *data, int c, size_t size) +{ + volatile unsigned volatile_zero = 0; + volatile char *vdata = (volatile char *) data; + + /* This is based on a nice trick for safe memset, + * sent by David Jacobson in the openssl-dev mailing list. + */ + + if (size > 0) + do + { + memset (data, c, size); + } + while (vdata[volatile_zero] != c); +} + +#endif /* GSTR_H */ diff --git a/grub-core/lib/libtasn1/lib/int.h b/grub-core/lib/libtasn1/lib/int.h new file mode 100644 index 000000000..d94d51c8c --- /dev/null +++ b/grub-core/lib/libtasn1/lib/int.h @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2002-2022 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef INT_H +# define INT_H + +# ifdef HAVE_CONFIG_H +# include +# endif + +# include +# include +# include +# include + +# ifdef HAVE_SYS_TYPES_H +# include +# endif + +# include + +# define ASN1_SMALL_VALUE_SIZE 16 + +/* This structure is also in libtasn1.h, but then contains less + fields. You cannot make any modifications to these first fields + without breaking ABI. */ +struct asn1_node_st +{ + /* public fields: */ + char name[ASN1_MAX_NAME_SIZE + 1]; /* Node name */ + unsigned int name_hash; + unsigned int type; /* Node type */ + unsigned char *value; /* Node value */ + int value_len; + asn1_node down; /* Pointer to the son node */ + asn1_node right; /* Pointer to the brother node */ + asn1_node left; /* Pointer to the next list element */ + /* private fields: */ + unsigned char small_value[ASN1_SMALL_VALUE_SIZE]; /* For small values */ + + /* values used during decoding/coding */ + int tmp_ival; + unsigned start; /* the start of the DER sequence - if decoded */ + unsigned end; /* the end of the DER sequence - if decoded */ +}; + +typedef struct tag_and_class_st +{ + unsigned tag; + unsigned class; + const char *desc; +} tag_and_class_st; + +/* the types that are handled in _asn1_tags */ +# define CASE_HANDLED_ETYPES \ + case ASN1_ETYPE_NULL: \ + case ASN1_ETYPE_BOOLEAN: \ + case ASN1_ETYPE_INTEGER: \ + case ASN1_ETYPE_ENUMERATED: \ + case ASN1_ETYPE_OBJECT_ID: \ + case ASN1_ETYPE_OCTET_STRING: \ + case ASN1_ETYPE_GENERALSTRING: \ + case ASN1_ETYPE_NUMERIC_STRING: \ + case ASN1_ETYPE_IA5_STRING: \ + case ASN1_ETYPE_TELETEX_STRING: \ + case ASN1_ETYPE_PRINTABLE_STRING: \ + case ASN1_ETYPE_UNIVERSAL_STRING: \ + case ASN1_ETYPE_BMP_STRING: \ + case ASN1_ETYPE_UTF8_STRING: \ + case ASN1_ETYPE_VISIBLE_STRING: \ + case ASN1_ETYPE_BIT_STRING: \ + case ASN1_ETYPE_SEQUENCE: \ + case ASN1_ETYPE_SEQUENCE_OF: \ + case ASN1_ETYPE_SET: \ + case ASN1_ETYPE_UTC_TIME: \ + case ASN1_ETYPE_GENERALIZED_TIME: \ + case ASN1_ETYPE_SET_OF + +# define ETYPE_TAG(etype) (_asn1_tags[etype].tag) +# define ETYPE_CLASS(etype) (_asn1_tags[etype].class) +# define ETYPE_OK(etype) (((etype) != ASN1_ETYPE_INVALID && \ + (etype) < _asn1_tags_size && \ + _asn1_tags[(etype)].desc != NULL)?1:0) + +# define ETYPE_IS_STRING(etype) ((etype == ASN1_ETYPE_GENERALSTRING || \ + etype == ASN1_ETYPE_NUMERIC_STRING || etype == ASN1_ETYPE_IA5_STRING || \ + etype == ASN1_ETYPE_TELETEX_STRING || etype == ASN1_ETYPE_PRINTABLE_STRING || \ + etype == ASN1_ETYPE_UNIVERSAL_STRING || etype == ASN1_ETYPE_BMP_STRING || \ + etype == ASN1_ETYPE_UTF8_STRING || etype == ASN1_ETYPE_VISIBLE_STRING || \ + etype == ASN1_ETYPE_OCTET_STRING)?1:0) + +extern unsigned int _asn1_tags_size; +extern const tag_and_class_st _asn1_tags[]; + +# define _asn1_strlen(s) strlen((const char *) s) +# define _asn1_strtol(n,e,b) strtol((const char *) n, e, b) +# define _asn1_strtoul(n,e,b) strtoul((const char *) n, e, b) +# define _asn1_strcmp(a,b) strcmp((const char *)a, (const char *)b) +# define _asn1_strcpy(a,b) strcpy((char *)a, (const char *)b) +# define _asn1_strcat(a,b) strcat((char *)a, (const char *)b) + +# if SIZEOF_UNSIGNED_LONG_INT == 8 +# define _asn1_strtou64(n,e,b) strtoul((const char *) n, e, b) +# else +# define _asn1_strtou64(n,e,b) strtoull((const char *) n, e, b) +# endif + +# define MAX_LOG_SIZE 1024 /* maximum number of characters of a log message */ + +/* Define used for visiting trees. */ +# define UP 1 +# define RIGHT 2 +# define DOWN 3 + +/***********************************************************************/ +/* List of constants to better specify the type of typedef asn1_node_st. */ +/***********************************************************************/ +/* Used with TYPE_TAG */ +# define CONST_UNIVERSAL (1U<<8) +# define CONST_PRIVATE (1U<<9) +# define CONST_APPLICATION (1U<<10) +# define CONST_EXPLICIT (1U<<11) +# define CONST_IMPLICIT (1U<<12) + +# define CONST_TAG (1U<<13) /* Used in ASN.1 assignement */ +# define CONST_OPTION (1U<<14) +# define CONST_DEFAULT (1U<<15) +# define CONST_TRUE (1U<<16) +# define CONST_FALSE (1U<<17) + +# define CONST_LIST (1U<<18) /* Used with TYPE_INTEGER and TYPE_BIT_STRING */ +# define CONST_MIN_MAX (1U<<19) + +# define CONST_1_PARAM (1U<<20) + +# define CONST_SIZE (1U<<21) + +# define CONST_DEFINED_BY (1U<<22) + +/* Those two are deprecated and used for backwards compatibility */ +# define CONST_GENERALIZED (1U<<23) +# define CONST_UTC (1U<<24) + +/* #define CONST_IMPORTS (1U<<25) */ + +# define CONST_NOT_USED (1U<<26) +# define CONST_SET (1U<<27) +# define CONST_ASSIGN (1U<<28) + +# define CONST_DOWN (1U<<29) +# define CONST_RIGHT (1U<<30) + + +# define ASN1_ETYPE_TIME 17 +/****************************************/ +/* Returns the first 8 bits. */ +/* Used with the field type of asn1_node_st */ +/****************************************/ +inline static unsigned int +type_field (unsigned int ntype) +{ + return (ntype & 0xff); +} + +/* To convert old types from a static structure */ +inline static unsigned int +convert_old_type (unsigned int ntype) +{ + unsigned int type = ntype & 0xff; + if (type == ASN1_ETYPE_TIME) + { + if (ntype & CONST_UTC) + type = ASN1_ETYPE_UTC_TIME; + else + type = ASN1_ETYPE_GENERALIZED_TIME; + + ntype &= ~(CONST_UTC | CONST_GENERALIZED); + ntype &= 0xffffff00; + ntype |= type; + + return ntype; + } + else + return ntype; +} + +static inline void * +_asn1_realloc (void *ptr, size_t size) +{ + void *ret; + + if (size == 0) + return ptr; + + ret = realloc (ptr, size); + if (ret == NULL) + { + free (ptr); + } + return ret; +} + +#endif /* INT_H */ diff --git a/grub-core/lib/libtasn1/lib/parser_aux.c b/grub-core/lib/libtasn1/lib/parser_aux.c new file mode 100644 index 000000000..c05bd2339 --- /dev/null +++ b/grub-core/lib/libtasn1/lib/parser_aux.c @@ -0,0 +1,1178 @@ +/* + * Copyright (C) 2000-2022 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include /* WORD_BIT */ + +#include "int.h" +#include "parser_aux.h" +#include "gstr.h" +#include "structure.h" +#include "element.h" +#include "c-ctype.h" + +char _asn1_identifierMissing[ASN1_MAX_NAME_SIZE + 1]; /* identifier name not found */ + +/* Return a hash of the N bytes of X using the method described by + Bruno Haible in https://www.haible.de/bruno/hashfunc.html. + Note that while many hash functions reduce their result via modulo + to a 0..table_size-1 range, this function does not do that. + + This implementation has been changed from size_t -> unsigned int. */ + +#ifdef __clang__ +__attribute__((no_sanitize ("integer"))) +#endif + _GL_ATTRIBUTE_PURE static unsigned int _asn1_hash_name (const char *x) +{ + const unsigned char *s = (unsigned char *) x; + unsigned h = 0; + + while (*s) + h = (*s++) + ((h << 9) | (h >> (WORD_BIT - 9))); + + return h; +} + +/******************************************************/ +/* Function : _asn1_add_static_node */ +/* Description: creates a new NODE_ASN element and */ +/* puts it in the list pointed by e_list. */ +/* Parameters: */ +/* e_list: of type list_type; must be NULL initially */ +/* type: type of the new element (see ASN1_ETYPE_ */ +/* and CONST_ constants). */ +/* Return: pointer to the new element. */ +/******************************************************/ +asn1_node +_asn1_add_static_node (list_type ** e_list, unsigned int type) +{ + list_type *p; + asn1_node punt; + + punt = calloc (1, sizeof (struct asn1_node_st)); + if (punt == NULL) + return NULL; + + p = malloc (sizeof (list_type)); + if (p == NULL) + { + free (punt); + return NULL; + } + + p->node = punt; + p->next = *e_list; + *e_list = p; + + punt->type = type; + + return punt; +} + +static int +_asn1_add_static_node2 (list_type ** e_list, asn1_node node) +{ + list_type *p; + + p = malloc (sizeof (list_type)); + if (p == NULL) + { + return -1; + } + + p->node = node; + p->next = *e_list; + *e_list = p; + + return 0; +} + +/** + * asn1_find_node: + * @pointer: NODE_ASN element pointer. + * @name: null terminated string with the element's name to find. + * + * Searches for an element called @name starting from @pointer. The + * name is composed by different identifiers separated by dots. When + * *@pointer has a name, the first identifier must be the name of + * *@pointer, otherwise it must be the name of one child of *@pointer. + * + * Returns: the search result, or %NULL if not found. + **/ +asn1_node +asn1_find_node (asn1_node_const pointer, const char *name) +{ + asn1_node_const p; + char *n_end, n[ASN1_MAX_NAME_SIZE + 1]; + const char *n_start; + unsigned int nsize; + unsigned int nhash; + + if (pointer == NULL) + return NULL; + + if (name == NULL) + return NULL; + + p = pointer; + n_start = name; + + if (name[0] == '?' && name[1] == 'C' && p->name[0] == '?') + { /* ?CURRENT */ + n_start = strchr (n_start, '.'); + if (n_start) + n_start++; + } + else if (p->name[0] != 0) + { /* has *pointer got a name ? */ + n_end = strchr (n_start, '.'); /* search the first dot */ + if (n_end) + { + nsize = n_end - n_start; + if (nsize >= sizeof (n)) + return NULL; + + memcpy (n, n_start, nsize); + n[nsize] = 0; + n_start = n_end; + n_start++; + + nhash = _asn1_hash_name (n); + } + else + { + _asn1_str_cpy (n, sizeof (n), n_start); + nhash = _asn1_hash_name (n); + + n_start = NULL; + } + + while (p) + { + if (nhash == p->name_hash && (!strcmp (p->name, n))) + break; + else + p = p->right; + } /* while */ + + if (p == NULL) + return NULL; + } + else + { /* *pointer doesn't have a name */ + if (n_start[0] == 0) + return (asn1_node) p; + } + + while (n_start) + { /* Has the end of NAME been reached? */ + n_end = strchr (n_start, '.'); /* search the next dot */ + if (n_end) + { + nsize = n_end - n_start; + if (nsize >= sizeof (n)) + return NULL; + + memcpy (n, n_start, nsize); + n[nsize] = 0; + n_start = n_end; + n_start++; + + nhash = _asn1_hash_name (n); + } + else + { + _asn1_str_cpy (n, sizeof (n), n_start); + nhash = _asn1_hash_name (n); + n_start = NULL; + } + + if (p->down == NULL) + return NULL; + + p = p->down; + if (p == NULL) + return NULL; + + /* The identifier "?LAST" indicates the last element + in the right chain. */ + if (n[0] == '?' && n[1] == 'L') /* ?LAST */ + { + while (p->right) + p = p->right; + } + else + { /* no "?LAST" */ + while (p) + { + if (p->name_hash == nhash && !strcmp (p->name, n)) + break; + else + p = p->right; + } + } + if (p == NULL) + return NULL; + } /* while */ + + return (asn1_node) p; +} + + +/******************************************************************/ +/* Function : _asn1_set_value */ +/* Description: sets the field VALUE in a NODE_ASN element. The */ +/* previous value (if exist) will be lost */ +/* Parameters: */ +/* node: element pointer. */ +/* value: pointer to the value that you want to set. */ +/* len: character number of value. */ +/* Return: pointer to the NODE_ASN element. */ +/******************************************************************/ +asn1_node +_asn1_set_value (asn1_node node, const void *value, unsigned int len) +{ + if (node == NULL) + return node; + if (node->value) + { + if (node->value != node->small_value) + free (node->value); + node->value = NULL; + node->value_len = 0; + } + + if (!len) + return node; + + if (len < sizeof (node->small_value)) + { + node->value = node->small_value; + } + else + { + node->value = malloc (len); + if (node->value == NULL) + return NULL; + } + node->value_len = len; + + memcpy (node->value, value, len); + return node; +} + +/******************************************************************/ +/* Function : _asn1_set_value_lv */ +/* Description: sets the field VALUE in a NODE_ASN element. The */ +/* previous value (if exist) will be lost. The value */ +/* given is stored as an length-value format (LV */ +/* Parameters: */ +/* node: element pointer. */ +/* value: pointer to the value that you want to set. */ +/* len: character number of value. */ +/* Return: pointer to the NODE_ASN element. */ +/******************************************************************/ +asn1_node +_asn1_set_value_lv (asn1_node node, const void *value, unsigned int len) +{ + int len2; + void *temp; + + if (node == NULL) + return node; + + asn1_length_der (len, NULL, &len2); + temp = malloc (len + len2); + if (temp == NULL) + return NULL; + + asn1_octet_der (value, len, temp, &len2); + return _asn1_set_value_m (node, temp, len2); +} + +/* the same as _asn1_set_value except that it sets an already malloc'ed + * value. + */ +asn1_node +_asn1_set_value_m (asn1_node node, void *value, unsigned int len) +{ + if (node == NULL) + return node; + + if (node->value) + { + if (node->value != node->small_value) + free (node->value); + node->value = NULL; + node->value_len = 0; + } + + if (!len) + return node; + + node->value = value; + node->value_len = len; + + return node; +} + +/******************************************************************/ +/* Function : _asn1_append_value */ +/* Description: appends to the field VALUE in a NODE_ASN element. */ +/* */ +/* Parameters: */ +/* node: element pointer. */ +/* value: pointer to the value that you want to be appended. */ +/* len: character number of value. */ +/* Return: pointer to the NODE_ASN element. */ +/******************************************************************/ +asn1_node +_asn1_append_value (asn1_node node, const void *value, unsigned int len) +{ + if (node == NULL) + return node; + + if (node->value == NULL) + return _asn1_set_value (node, value, len); + + if (len == 0) + return node; + + if (node->value == node->small_value) + { + /* value is in node */ + int prev_len = node->value_len; + node->value_len += len; + node->value = malloc (node->value_len); + if (node->value == NULL) + { + node->value_len = 0; + return NULL; + } + + if (prev_len > 0) + memcpy (node->value, node->small_value, prev_len); + + memcpy (&node->value[prev_len], value, len); + + return node; + } + else /* if (node->value != NULL && node->value != node->small_value) */ + { + /* value is allocated */ + int prev_len = node->value_len; + node->value_len += len; + + node->value = _asn1_realloc (node->value, node->value_len); + if (node->value == NULL) + { + node->value_len = 0; + return NULL; + } + + memcpy (&node->value[prev_len], value, len); + + return node; + } +} + +/******************************************************************/ +/* Function : _asn1_set_name */ +/* Description: sets the field NAME in a NODE_ASN element. The */ +/* previous value (if exist) will be lost */ +/* Parameters: */ +/* node: element pointer. */ +/* name: a null terminated string with the name that you want */ +/* to set. */ +/* Return: pointer to the NODE_ASN element. */ +/******************************************************************/ +asn1_node +_asn1_set_name (asn1_node node, const char *name) +{ + if (node == NULL) + return node; + + _asn1_str_cpy (node->name, sizeof (node->name), name ? name : ""); + node->name_hash = _asn1_hash_name (node->name); + + return node; +} + +/******************************************************************/ +/* Function : _asn1_cpy_name */ +/* Description: copies the field NAME in a NODE_ASN element. */ +/* Parameters: */ +/* dst: a dest element pointer. */ +/* src: a source element pointer. */ +/* Return: pointer to the NODE_ASN element. */ +/******************************************************************/ +asn1_node +_asn1_cpy_name (asn1_node dst, asn1_node_const src) +{ + if (dst == NULL) + return dst; + + if (src == NULL) + { + dst->name[0] = 0; + dst->name_hash = _asn1_hash_name (dst->name); + return dst; + } + + _asn1_str_cpy (dst->name, sizeof (dst->name), src->name); + dst->name_hash = src->name_hash; + + return dst; +} + +/******************************************************************/ +/* Function : _asn1_set_right */ +/* Description: sets the field RIGHT in a NODE_ASN element. */ +/* Parameters: */ +/* node: element pointer. */ +/* right: pointer to a NODE_ASN element that you want be pointed*/ +/* by NODE. */ +/* Return: pointer to *NODE. */ +/******************************************************************/ +asn1_node +_asn1_set_right (asn1_node node, asn1_node right) +{ + if (node == NULL) + return node; + node->right = right; + if (right) + right->left = node; + return node; +} + + +/******************************************************************/ +/* Function : _asn1_get_last_right */ +/* Description: return the last element along the right chain. */ +/* Parameters: */ +/* node: starting element pointer. */ +/* Return: pointer to the last element along the right chain. */ +/******************************************************************/ +asn1_node +_asn1_get_last_right (asn1_node_const node) +{ + asn1_node_const p; + + if (node == NULL) + return NULL; + p = node; + while (p->right) + p = p->right; + return (asn1_node) p; +} + +/******************************************************************/ +/* Function : _asn1_remove_node */ +/* Description: gets free the memory allocated for an NODE_ASN */ +/* element (not the elements pointed by it). */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* flags: ASN1_DELETE_FLAG_* */ +/******************************************************************/ +void +_asn1_remove_node (asn1_node node, unsigned int flags) +{ + if (node == NULL) + return; + + if (node->value != NULL) + { + if (flags & ASN1_DELETE_FLAG_ZEROIZE) + { + safe_memset (node->value, 0, node->value_len); + } + + if (node->value != node->small_value) + free (node->value); + } + free (node); +} + +/******************************************************************/ +/* Function : _asn1_find_up */ +/* Description: return the father of the NODE_ASN element. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* Return: Null if not found. */ +/******************************************************************/ +asn1_node +_asn1_find_up (asn1_node_const node) +{ + asn1_node_const p; + + if (node == NULL) + return NULL; + + p = node; + + while ((p->left != NULL) && (p->left->right == p)) + p = p->left; + + return p->left; +} + +static unsigned +_asn1_is_up (asn1_node_const up_cand, asn1_node_const down) +{ + asn1_node_const d, u; + + if (up_cand == NULL || down == NULL) + return 0; + + d = down; + + while ((u = _asn1_find_up (d)) != NULL && u != d) + { + if (u == up_cand) + return 1; + d = u; + } + + return 0; +} + +/******************************************************************/ +/* Function : _asn1_delete_node_from_list */ +/* Description: deletes the list element given */ +/******************************************************************/ +void +_asn1_delete_node_from_list (list_type * list, asn1_node node) +{ + list_type *p = list; + + while (p) + { + if (p->node == node) + p->node = NULL; + p = p->next; + } +} + +/******************************************************************/ +/* Function : _asn1_delete_list */ +/* Description: deletes the list elements (not the elements */ +/* pointed by them). */ +/******************************************************************/ +void +_asn1_delete_list (list_type * e_list) +{ + list_type *p; + + while (e_list) + { + p = e_list; + e_list = e_list->next; + free (p); + } +} + +/******************************************************************/ +/* Function : _asn1_delete_list_and nodes */ +/* Description: deletes the list elements and the elements */ +/* pointed by them. */ +/******************************************************************/ +void +_asn1_delete_list_and_nodes (list_type * e_list) +{ + list_type *p; + + while (e_list) + { + p = e_list; + e_list = e_list->next; + _asn1_remove_node (p->node, 0); + free (p); + } +} + + +char * +_asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE]) +{ + uint64_t d, r; + char temp[LTOSTR_MAX_SIZE]; + int count, k, start; + uint64_t val; + + if (v < 0) + { + str[0] = '-'; + start = 1; + val = -((uint64_t) v); + } + else + { + val = v; + start = 0; + } + + count = 0; + do + { + d = val / 10; + r = val - d * 10; + temp[start + count] = '0' + (char) r; + count++; + val = d; + } + while (val && ((start + count) < LTOSTR_MAX_SIZE - 1)); + + for (k = 0; k < count; k++) + str[k + start] = temp[start + count - k - 1]; + str[count + start] = 0; + return str; +} + + +/******************************************************************/ +/* Function : _asn1_change_integer_value */ +/* Description: converts into DER coding the value assign to an */ +/* INTEGER constant. */ +/* Parameters: */ +/* node: root of an ASN1element. */ +/* Return: */ +/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ +/* otherwise ASN1_SUCCESS */ +/******************************************************************/ +int +_asn1_change_integer_value (asn1_node node) +{ + asn1_node p; + unsigned char val[SIZEOF_UNSIGNED_LONG_INT]; + unsigned char val2[SIZEOF_UNSIGNED_LONG_INT + 1]; + int len; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + while (p) + { + if ((type_field (p->type) == ASN1_ETYPE_INTEGER) + && (p->type & CONST_ASSIGN)) + { + if (p->value) + { + _asn1_convert_integer (p->value, val, sizeof (val), &len); + asn1_octet_der (val, len, val2, &len); + _asn1_set_value (p, val2, len); + } + } + + if (p->down) + { + p = p->down; + } + else + { + if (p == node) + p = NULL; + else if (p->right) + p = p->right; + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == node) + { + p = NULL; + break; + } + if (p && p->right) + { + p = p->right; + break; + } + } + } + } + } + + return ASN1_SUCCESS; +} + +#define MAX_CONSTANTS 1024 +/******************************************************************/ +/* Function : _asn1_expand_object_id */ +/* Description: expand the IDs of an OBJECT IDENTIFIER constant. */ +/* Parameters: */ +/* list: root of an object list */ +/* node: root of an ASN1 element. */ +/* Return: */ +/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ +/* otherwise ASN1_SUCCESS */ +/******************************************************************/ +int +_asn1_expand_object_id (list_type ** list, asn1_node node) +{ + asn1_node p, p2, p3, p4, p5; + char name_root[ASN1_MAX_NAME_SIZE], name2[2 * ASN1_MAX_NAME_SIZE + 1]; + int move, tlen, tries; + unsigned max_constants; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + _asn1_str_cpy (name_root, sizeof (name_root), node->name); + + p = node; + move = DOWN; + tries = 0; + + while (!((p == node) && (move == UP))) + { + if (move != UP) + { + if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) + && (p->type & CONST_ASSIGN)) + { + p2 = p->down; + if (p2 && (type_field (p2->type) == ASN1_ETYPE_CONSTANT)) + { + if (p2->value && !c_isdigit (p2->value[0])) + { + _asn1_str_cpy (name2, sizeof (name2), name_root); + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), + (char *) p2->value); + p3 = asn1_find_node (node, name2); + if (!p3 || _asn1_is_up (p2, p3) || + (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) || + !(p3->type & CONST_ASSIGN)) + return ASN1_ELEMENT_NOT_FOUND; + + _asn1_set_down (p, p2->right); + if (p2->down) + _asn1_delete_structure (*list, &p2->down, 0); + _asn1_delete_node_from_list (*list, p2); + _asn1_remove_node (p2, 0); + p2 = p; + p4 = p3->down; + max_constants = 0; + while (p4) + { + if (type_field (p4->type) == ASN1_ETYPE_CONSTANT) + { + max_constants++; + if (max_constants == MAX_CONSTANTS) + return ASN1_RECURSION; + + p5 = + _asn1_add_single_node (ASN1_ETYPE_CONSTANT); + _asn1_set_name (p5, p4->name); + if (p4->value) + { + tlen = _asn1_strlen (p4->value); + if (tlen > 0) + _asn1_set_value (p5, p4->value, tlen + 1); + } + _asn1_add_static_node2 (list, p5); + + if (p2 == p) + { + _asn1_set_right (p5, p->down); + _asn1_set_down (p, p5); + } + else + { + _asn1_set_right (p5, p2->right); + _asn1_set_right (p2, p5); + } + p2 = p5; + } + p4 = p4->right; + } + move = DOWN; + + tries++; + if (tries >= EXPAND_OBJECT_ID_MAX_RECURSION) + return ASN1_RECURSION; + + continue; + } + } + } + move = DOWN; + } + else + move = RIGHT; + + tries = 0; + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + + if (p == node) + { + move = UP; + continue; + } + + if (move == RIGHT) + { + if (p && p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + /*******************************/ + /* expand DEFAULT */ + /*******************************/ + p = node; + move = DOWN; + + while (!((p == node) && (move == UP))) + { + if (move != UP) + { + if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) && + (p->type & CONST_DEFAULT)) + { + p2 = p->down; + if (p2 && (type_field (p2->type) == ASN1_ETYPE_DEFAULT)) + { + _asn1_str_cpy (name2, sizeof (name2), name_root); + _asn1_str_cat (name2, sizeof (name2), "."); + if (p2->value) + _asn1_str_cat (name2, sizeof (name2), (char *) p2->value); + p3 = asn1_find_node (node, name2); + if (!p3 || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) + || !(p3->type & CONST_ASSIGN)) + return ASN1_ELEMENT_NOT_FOUND; + p4 = p3->down; + name2[0] = 0; + while (p4) + { + if (type_field (p4->type) == ASN1_ETYPE_CONSTANT) + { + if (p4->value == NULL) + return ASN1_VALUE_NOT_FOUND; + + if (name2[0]) + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), + (char *) p4->value); + } + p4 = p4->right; + } + tlen = strlen (name2); + if (tlen > 0) + _asn1_set_value (p2, name2, tlen + 1); + } + } + move = DOWN; + } + else + move = RIGHT; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + + if (p == node) + { + move = UP; + continue; + } + + if (move == RIGHT) + { + if (p && p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + return ASN1_SUCCESS; +} + + +/******************************************************************/ +/* Function : _asn1_type_set_config */ +/* Description: sets the CONST_SET and CONST_NOT_USED properties */ +/* in the fields of the SET elements. */ +/* Parameters: */ +/* node: root of an ASN1 element. */ +/* Return: */ +/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ +/* otherwise ASN1_SUCCESS */ +/******************************************************************/ +int +_asn1_type_set_config (asn1_node node) +{ + asn1_node p, p2; + int move; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + move = DOWN; + + while (!((p == node) && (move == UP))) + { + if (move != UP) + { + if (type_field (p->type) == ASN1_ETYPE_SET) + { + p2 = p->down; + while (p2) + { + if (type_field (p2->type) != ASN1_ETYPE_TAG) + p2->type |= CONST_SET | CONST_NOT_USED; + p2 = p2->right; + } + } + move = DOWN; + } + else + move = RIGHT; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + + if (p == node) + { + move = UP; + continue; + } + + if (move == RIGHT) + { + if (p && p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + return ASN1_SUCCESS; +} + + +/******************************************************************/ +/* Function : _asn1_check_identifier */ +/* Description: checks the definitions of all the identifiers */ +/* and the first element of an OBJECT_ID (e.g. {pkix 0 4}). */ +/* The _asn1_identifierMissing global variable is filled if */ +/* necessary. */ +/* Parameters: */ +/* node: root of an ASN1 element. */ +/* Return: */ +/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ +/* ASN1_IDENTIFIER_NOT_FOUND if an identifier is not defined, */ +/* otherwise ASN1_SUCCESS */ +/******************************************************************/ +int +_asn1_check_identifier (asn1_node_const node) +{ + asn1_node_const p, p2; + char name2[ASN1_MAX_NAME_SIZE * 2 + 2]; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + while (p) + { + if (p->value && type_field (p->type) == ASN1_ETYPE_IDENTIFIER) + { + _asn1_str_cpy (name2, sizeof (name2), node->name); + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), (char *) p->value); + p2 = asn1_find_node (node, name2); + if (p2 == NULL) + { + if (p->value) + _asn1_str_cpy (_asn1_identifierMissing, + sizeof (_asn1_identifierMissing), + (char *) p->value); + else + _asn1_strcpy (_asn1_identifierMissing, "(null)"); + return ASN1_IDENTIFIER_NOT_FOUND; + } + } + else if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) && + (p->type & CONST_DEFAULT)) + { + p2 = p->down; + if (p2 && (type_field (p2->type) == ASN1_ETYPE_DEFAULT)) + { + _asn1_str_cpy (name2, sizeof (name2), node->name); + if (p2->value) + { + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), (char *) p2->value); + _asn1_str_cpy (_asn1_identifierMissing, + sizeof (_asn1_identifierMissing), + (char *) p2->value); + } + else + _asn1_strcpy (_asn1_identifierMissing, "(null)"); + + p2 = asn1_find_node (node, name2); + if (!p2 || (type_field (p2->type) != ASN1_ETYPE_OBJECT_ID) || + !(p2->type & CONST_ASSIGN)) + return ASN1_IDENTIFIER_NOT_FOUND; + else + _asn1_identifierMissing[0] = 0; + } + } + else if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) && + (p->type & CONST_ASSIGN)) + { + p2 = p->down; + if (p2 && (type_field (p2->type) == ASN1_ETYPE_CONSTANT)) + { + if (p2->value && !c_isdigit (p2->value[0])) + { + _asn1_str_cpy (name2, sizeof (name2), node->name); + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), (char *) p2->value); + _asn1_str_cpy (_asn1_identifierMissing, + sizeof (_asn1_identifierMissing), + (char *) p2->value); + + p2 = asn1_find_node (node, name2); + if (!p2 || (type_field (p2->type) != ASN1_ETYPE_OBJECT_ID) + || !(p2->type & CONST_ASSIGN)) + return ASN1_IDENTIFIER_NOT_FOUND; + else + _asn1_identifierMissing[0] = 0; + } + } + } + + if (p->down) + { + p = p->down; + } + else if (p->right) + p = p->right; + else + { + while (p) + { + p = _asn1_find_up (p); + if (p == node) + { + p = NULL; + break; + } + if (p && p->right) + { + p = p->right; + break; + } + } + } + } + + return ASN1_SUCCESS; +} + + +/******************************************************************/ +/* Function : _asn1_set_default_tag */ +/* Description: sets the default IMPLICIT or EXPLICIT property in */ +/* the tagged elements that don't have this declaration. */ +/* Parameters: */ +/* node: pointer to a DEFINITIONS element. */ +/* Return: */ +/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL or not a pointer to */ +/* a DEFINITIONS element, */ +/* otherwise ASN1_SUCCESS */ +/******************************************************************/ +int +_asn1_set_default_tag (asn1_node node) +{ + asn1_node p; + + if ((node == NULL) || (type_field (node->type) != ASN1_ETYPE_DEFINITIONS)) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + while (p) + { + if ((type_field (p->type) == ASN1_ETYPE_TAG) && + !(p->type & CONST_EXPLICIT) && !(p->type & CONST_IMPLICIT)) + { + if (node->type & CONST_EXPLICIT) + p->type |= CONST_EXPLICIT; + else + p->type |= CONST_IMPLICIT; + } + + if (p->down) + { + p = p->down; + } + else if (p->right) + p = p->right; + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == node) + { + p = NULL; + break; + } + if (p && p->right) + { + p = p->right; + break; + } + } + } + } + + return ASN1_SUCCESS; +} diff --git a/grub-core/lib/libtasn1/lib/parser_aux.h b/grub-core/lib/libtasn1/lib/parser_aux.h new file mode 100644 index 000000000..3eac1fa30 --- /dev/null +++ b/grub-core/lib/libtasn1/lib/parser_aux.h @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2000-2022 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _PARSER_AUX_H +# define _PARSER_AUX_H + +/***********************************************/ +/* Type: list_type */ +/* Description: type used in the list during */ +/* the structure creation. */ +/***********************************************/ +typedef struct list_struct +{ + asn1_node node; + struct list_struct *next; +} list_type; + +/***************************************/ +/* Functions used by ASN.1 parser */ +/***************************************/ +asn1_node _asn1_add_static_node (list_type ** e_list, unsigned int type); + +void _asn1_delete_list (list_type * e_list); + +void _asn1_delete_list_and_nodes (list_type * e_list); + +void _asn1_delete_node_from_list (list_type * list, asn1_node node); + +asn1_node +_asn1_set_value (asn1_node node, const void *value, unsigned int len); + +asn1_node _asn1_set_value_m (asn1_node node, void *value, unsigned int len); + +asn1_node +_asn1_set_value_lv (asn1_node node, const void *value, unsigned int len); + +asn1_node +_asn1_append_value (asn1_node node, const void *value, unsigned int len); + +asn1_node _asn1_set_name (asn1_node node, const char *name); + +asn1_node _asn1_cpy_name (asn1_node dst, asn1_node_const src); + +asn1_node _asn1_set_right (asn1_node node, asn1_node right); + +asn1_node _asn1_get_last_right (asn1_node_const node); + +void _asn1_remove_node (asn1_node node, unsigned int flags); + +/* Max 64-bit integer length is 20 chars + 1 for sign + 1 for null termination */ +# define LTOSTR_MAX_SIZE 22 +char *_asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE]); + +asn1_node _asn1_find_up (asn1_node_const node); + +int _asn1_change_integer_value (asn1_node node); + +# define EXPAND_OBJECT_ID_MAX_RECURSION 16 +int _asn1_expand_object_id (list_type ** list, asn1_node node); + +int _asn1_type_set_config (asn1_node node); + +int _asn1_check_identifier (asn1_node_const node); + +int _asn1_set_default_tag (asn1_node node); + +/******************************************************************/ +/* Function : _asn1_get_right */ +/* Description: returns the element pointed by the RIGHT field of */ +/* a NODE_ASN element. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* Return: field RIGHT of NODE. */ +/******************************************************************/ +inline static asn1_node +_asn1_get_right (asn1_node_const node) +{ + if (node == NULL) + return NULL; + return node->right; +} + +/******************************************************************/ +/* Function : _asn1_set_down */ +/* Description: sets the field DOWN in a NODE_ASN element. */ +/* Parameters: */ +/* node: element pointer. */ +/* down: pointer to a NODE_ASN element that you want be pointed */ +/* by NODE. */ +/* Return: pointer to *NODE. */ +/******************************************************************/ +inline static asn1_node +_asn1_set_down (asn1_node node, asn1_node down) +{ + if (node == NULL) + return node; + node->down = down; + if (down) + down->left = node; + return node; +} + +/******************************************************************/ +/* Function : _asn1_get_down */ +/* Description: returns the element pointed by the DOWN field of */ +/* a NODE_ASN element. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* Return: field DOWN of NODE. */ +/******************************************************************/ +inline static asn1_node +_asn1_get_down (asn1_node_const node) +{ + if (node == NULL) + return NULL; + return node->down; +} + +/******************************************************************/ +/* Function : _asn1_get_name */ +/* Description: returns the name of a NODE_ASN element. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* Return: a null terminated string. */ +/******************************************************************/ +inline static char * +_asn1_get_name (asn1_node_const node) +{ + if (node == NULL) + return NULL; + return (char *) node->name; +} + +/******************************************************************/ +/* Function : _asn1_mod_type */ +/* Description: change the field TYPE of an NODE_ASN element. */ +/* The new value is the old one | (bitwise or) the */ +/* paramener VALUE. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* value: the integer value that must be or-ed with the current */ +/* value of field TYPE. */ +/* Return: NODE pointer. */ +/******************************************************************/ +inline static asn1_node +_asn1_mod_type (asn1_node node, unsigned int value) +{ + if (node == NULL) + return node; + node->type |= value; + return node; +} + +#endif diff --git a/grub-core/lib/libtasn1/lib/structure.c b/grub-core/lib/libtasn1/lib/structure.c new file mode 100644 index 000000000..512dd601f --- /dev/null +++ b/grub-core/lib/libtasn1/lib/structure.c @@ -0,0 +1,1225 @@ +/* + * Copyright (C) 2002-2022 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +/*****************************************************/ +/* File: structure.c */ +/* Description: Functions to create and delete an */ +/* ASN1 tree. */ +/*****************************************************/ + + +#include +#include +#include "parser_aux.h" +#include + + +extern char _asn1_identifierMissing[]; + + +/******************************************************/ +/* Function : _asn1_add_single_node */ +/* Description: creates a new NODE_ASN element. */ +/* Parameters: */ +/* type: type of the new element (see ASN1_ETYPE_ */ +/* and CONST_ constants). */ +/* Return: pointer to the new element. */ +/******************************************************/ +asn1_node +_asn1_add_single_node (unsigned int type) +{ + asn1_node punt; + + punt = calloc (1, sizeof (struct asn1_node_st)); + if (punt == NULL) + return NULL; + + punt->type = type; + + return punt; +} + + +/******************************************************************/ +/* Function : _asn1_find_left */ +/* Description: returns the NODE_ASN element with RIGHT field that*/ +/* points the element NODE. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* Return: NULL if not found. */ +/******************************************************************/ +asn1_node +_asn1_find_left (asn1_node_const node) +{ + if ((node == NULL) || (node->left == NULL) || (node->left->down == node)) + return NULL; + + return node->left; +} + + +int +_asn1_create_static_structure (asn1_node_const pointer, + char *output_file_name, char *vector_name) +{ + FILE *file; + asn1_node_const p; + unsigned long t; + + file = fopen (output_file_name, "w"); + + if (file == NULL) + return ASN1_FILE_NOT_FOUND; + + fprintf (file, "#if HAVE_CONFIG_H\n"); + fprintf (file, "# include \"config.h\"\n"); + fprintf (file, "#endif\n\n"); + + fprintf (file, "#include \n\n"); + + fprintf (file, "const asn1_static_node %s[] = {\n", vector_name); + + p = pointer; + + while (p) + { + fprintf (file, " { "); + + if (p->name[0] != 0) + fprintf (file, "\"%s\", ", p->name); + else + fprintf (file, "NULL, "); + + t = p->type; + if (p->down) + t |= CONST_DOWN; + if (p->right) + t |= CONST_RIGHT; + + fprintf (file, "%lu, ", t); + + if (p->value) + fprintf (file, "\"%s\"},\n", p->value); + else + fprintf (file, "NULL },\n"); + + if (p->down) + { + p = p->down; + } + else if (p->right) + { + p = p->right; + } + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == pointer) + { + p = NULL; + break; + } + if (p->right) + { + p = p->right; + break; + } + } + } + } + + fprintf (file, " { NULL, 0, NULL }\n};\n"); + + fclose (file); + + return ASN1_SUCCESS; +} + + +/** + * asn1_array2tree: + * @array: specify the array that contains ASN.1 declarations + * @definitions: return the pointer to the structure created by + * *ARRAY ASN.1 declarations + * @errorDescription: return the error description. + * + * Creates the structures needed to manage the ASN.1 definitions. + * @array is a vector created by asn1_parser2array(). + * + * Returns: %ASN1_SUCCESS if structure was created correctly, + * %ASN1_ELEMENT_NOT_EMPTY if *@definitions not NULL, + * %ASN1_IDENTIFIER_NOT_FOUND if in the file there is an identifier + * that is not defined (see @errorDescription for more information), + * %ASN1_ARRAY_ERROR if the array pointed by @array is wrong. + **/ +int +asn1_array2tree (const asn1_static_node * array, asn1_node * definitions, + char *errorDescription) +{ + asn1_node p, p_last = NULL; + unsigned long k; + int move; + int result; + unsigned int type; + list_type *e_list = NULL; + + if (errorDescription) + errorDescription[0] = 0; + + if (*definitions != NULL) + return ASN1_ELEMENT_NOT_EMPTY; + + move = UP; + + for (k = 0; array[k].value || array[k].type || array[k].name; k++) + { + type = convert_old_type (array[k].type); + + p = _asn1_add_static_node (&e_list, type & (~CONST_DOWN)); + if (array[k].name) + _asn1_set_name (p, array[k].name); + if (array[k].value) + _asn1_set_value (p, array[k].value, strlen (array[k].value) + 1); + + if (*definitions == NULL) + *definitions = p; + + if (move == DOWN) + { + if (p_last && p_last->down) + _asn1_delete_structure (e_list, &p_last->down, 0); + _asn1_set_down (p_last, p); + } + else if (move == RIGHT) + { + if (p_last && p_last->right) + _asn1_delete_structure (e_list, &p_last->right, 0); + _asn1_set_right (p_last, p); + } + + p_last = p; + + if (type & CONST_DOWN) + move = DOWN; + else if (type & CONST_RIGHT) + move = RIGHT; + else + { + while (p_last != *definitions) + { + p_last = _asn1_find_up (p_last); + + if (p_last == NULL) + break; + + if (p_last->type & CONST_RIGHT) + { + p_last->type &= ~CONST_RIGHT; + move = RIGHT; + break; + } + } /* while */ + } + } /* while */ + + if (p_last == *definitions) + { + result = _asn1_check_identifier (*definitions); + if (result == ASN1_SUCCESS) + { + _asn1_change_integer_value (*definitions); + result = _asn1_expand_object_id (&e_list, *definitions); + } + } + else + { + result = ASN1_ARRAY_ERROR; + } + + if (errorDescription != NULL) + { + if (result == ASN1_IDENTIFIER_NOT_FOUND) + { + Estrcpy (errorDescription, ":: identifier '"); + Estrcat (errorDescription, _asn1_identifierMissing); + Estrcat (errorDescription, "' not found"); + } + else + errorDescription[0] = 0; + } + + if (result != ASN1_SUCCESS) + { + _asn1_delete_list_and_nodes (e_list); + *definitions = NULL; + } + else + _asn1_delete_list (e_list); + + return result; +} + +/** + * asn1_delete_structure: + * @structure: pointer to the structure that you want to delete. + * + * Deletes the structure *@structure. At the end, *@structure is set + * to NULL. + * + * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if + * *@structure was NULL. + **/ +int +asn1_delete_structure (asn1_node * structure) +{ + return _asn1_delete_structure (NULL, structure, 0); +} + +/** + * asn1_delete_structure2: + * @structure: pointer to the structure that you want to delete. + * @flags: additional flags (see %ASN1_DELETE_FLAG_ZEROIZE) + * + * Deletes the structure *@structure. At the end, *@structure is set + * to NULL. + * + * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if + * *@structure was NULL. + **/ +int +asn1_delete_structure2 (asn1_node * structure, unsigned int flags) +{ + return _asn1_delete_structure (NULL, structure, flags); +} + +int +_asn1_delete_structure (list_type * e_list, asn1_node * structure, + unsigned int flags) +{ + asn1_node p, p2, p3; + + if (*structure == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = *structure; + while (p) + { + if (p->down) + { + p = p->down; + } + else + { /* no down */ + p2 = p->right; + if (p != *structure) + { + p3 = _asn1_find_up (p); + _asn1_set_down (p3, p2); + if (e_list) + _asn1_delete_node_from_list (e_list, p); + _asn1_remove_node (p, flags); + p = p3; + } + else + { /* p==root */ + p3 = _asn1_find_left (p); + if (!p3) + { + p3 = _asn1_find_up (p); + if (p3) + _asn1_set_down (p3, p2); + else + { + if (p->right) + p->right->left = NULL; + } + } + else + _asn1_set_right (p3, p2); + if (e_list) + _asn1_delete_node_from_list (e_list, p); + _asn1_remove_node (p, flags); + p = NULL; + } + } + } + + *structure = NULL; + return ASN1_SUCCESS; +} + + +/** + * asn1_delete_element: + * @structure: pointer to the structure that contains the element you + * want to delete. + * @element_name: element's name you want to delete. + * + * Deletes the element named *@element_name inside *@structure. + * + * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if + * the @element_name was not found. + **/ +int +asn1_delete_element (asn1_node structure, const char *element_name) +{ + asn1_node p2, p3, source_node; + + source_node = asn1_find_node (structure, element_name); + + if (source_node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p2 = source_node->right; + p3 = _asn1_find_left (source_node); + if (!p3) + { + p3 = _asn1_find_up (source_node); + if (p3) + _asn1_set_down (p3, p2); + else if (source_node->right) + source_node->right->left = NULL; + } + else + _asn1_set_right (p3, p2); + + return asn1_delete_structure (&source_node); +} + +#ifndef __clang_analyzer__ +asn1_node +_asn1_copy_structure3 (asn1_node_const source_node) +{ + asn1_node_const p_s; + asn1_node dest_node, p_d, p_d_prev; + int move; + + if (source_node == NULL) + return NULL; + + dest_node = _asn1_add_single_node (source_node->type); + if (dest_node == NULL) + return dest_node; + + p_s = source_node; + p_d = dest_node; + + move = DOWN; + + do + { + if (move != UP) + { + if (p_s->name[0] != 0) + _asn1_cpy_name (p_d, p_s); + if (p_s->value) + _asn1_set_value (p_d, p_s->value, p_s->value_len); + if (p_s->down) + { + p_s = p_s->down; + p_d_prev = p_d; + p_d = _asn1_add_single_node (p_s->type); + _asn1_set_down (p_d_prev, p_d); + continue; + } + p_d->start = p_s->start; + p_d->end = p_s->end; + } + + if (p_s == source_node) + break; + + if (p_s->right) + { + move = RIGHT; + p_s = p_s->right; + p_d_prev = p_d; + p_d = _asn1_add_single_node (p_s->type); + _asn1_set_right (p_d_prev, p_d); + } + else + { + move = UP; + p_s = _asn1_find_up (p_s); + p_d = _asn1_find_up (p_d); + } + } + while (p_s != source_node); + return dest_node; +} +#else + +/* Non-production code */ +asn1_node +_asn1_copy_structure3 (asn1_node_const source_node) +{ + return NULL; +} +#endif /* __clang_analyzer__ */ + + +static asn1_node +_asn1_copy_structure2 (asn1_node_const root, const char *source_name) +{ + asn1_node source_node; + + source_node = asn1_find_node (root, source_name); + + return _asn1_copy_structure3 (source_node); + +} + + +static int +_asn1_type_choice_config (asn1_node node) +{ + asn1_node p, p2, p3, p4; + int move, tlen; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + move = DOWN; + + while (!((p == node) && (move == UP))) + { + if (move != UP) + { + if ((type_field (p->type) == ASN1_ETYPE_CHOICE) + && (p->type & CONST_TAG)) + { + p2 = p->down; + while (p2) + { + if (type_field (p2->type) != ASN1_ETYPE_TAG) + { + p2->type |= CONST_TAG; + p3 = _asn1_find_left (p2); + while (p3) + { + if (type_field (p3->type) == ASN1_ETYPE_TAG) + { + p4 = _asn1_add_single_node (p3->type); + tlen = _asn1_strlen (p3->value); + if (tlen > 0) + _asn1_set_value (p4, p3->value, tlen + 1); + _asn1_set_right (p4, p2->down); + _asn1_set_down (p2, p4); + } + p3 = _asn1_find_left (p3); + } + } + p2 = p2->right; + } + p->type &= ~(CONST_TAG); + p2 = p->down; + while (p2) + { + p3 = p2->right; + if (type_field (p2->type) == ASN1_ETYPE_TAG) + asn1_delete_structure (&p2); + p2 = p3; + } + } + move = DOWN; + } + else + move = RIGHT; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + + if (p == node) + { + move = UP; + continue; + } + + if (move == RIGHT) + { + if (p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + return ASN1_SUCCESS; +} + + +static int +_asn1_expand_identifier (asn1_node * node, asn1_node_const root) +{ + asn1_node p, p2, p3; + char name2[ASN1_MAX_NAME_SIZE + 2]; + int move; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = *node; + move = DOWN; + + while (!((p == *node) && (move == UP))) + { + if (move != UP) + { + if (type_field (p->type) == ASN1_ETYPE_IDENTIFIER) + { + snprintf (name2, sizeof (name2), "%s.%s", root->name, p->value); + p2 = _asn1_copy_structure2 (root, name2); + if (p2 == NULL) + { + return ASN1_IDENTIFIER_NOT_FOUND; + } + _asn1_cpy_name (p2, p); + p2->right = p->right; + p2->left = p->left; + if (p->right) + p->right->left = p2; + p3 = p->down; + if (p3) + { + while (p3->right) + p3 = p3->right; + _asn1_set_right (p3, p2->down); + _asn1_set_down (p2, p->down); + } + + p3 = _asn1_find_left (p); + if (p3) + _asn1_set_right (p3, p2); + else + { + p3 = _asn1_find_up (p); + if (p3) + _asn1_set_down (p3, p2); + else + { + p2->left = NULL; + } + } + + if (p->type & CONST_SIZE) + p2->type |= CONST_SIZE; + if (p->type & CONST_TAG) + p2->type |= CONST_TAG; + if (p->type & CONST_OPTION) + p2->type |= CONST_OPTION; + if (p->type & CONST_DEFAULT) + p2->type |= CONST_DEFAULT; + if (p->type & CONST_SET) + p2->type |= CONST_SET; + if (p->type & CONST_NOT_USED) + p2->type |= CONST_NOT_USED; + + if (p == *node) + *node = p2; + _asn1_remove_node (p, 0); + p = p2; + move = DOWN; + continue; + } + move = DOWN; + } + else + move = RIGHT; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + + if (p == *node) + { + move = UP; + continue; + } + + if (move == RIGHT) + { + if (p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + return ASN1_SUCCESS; +} + + +/** + * asn1_create_element: + * @definitions: pointer to the structure returned by "parser_asn1" function + * @source_name: the name of the type of the new structure (must be + * inside p_structure). + * @element: pointer to the structure created. + * + * Creates a structure of type @source_name. Example using + * "pkix.asn": + * + * rc = asn1_create_element(cert_def, "PKIX1.Certificate", certptr); + * + * Returns: %ASN1_SUCCESS if creation OK, %ASN1_ELEMENT_NOT_FOUND if + * @source_name is not known. + **/ +int +asn1_create_element (asn1_node_const definitions, const char *source_name, + asn1_node * element) +{ + asn1_node dest_node; + int res; + + dest_node = _asn1_copy_structure2 (definitions, source_name); + + if (dest_node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + _asn1_set_name (dest_node, ""); + + res = _asn1_expand_identifier (&dest_node, definitions); + _asn1_type_choice_config (dest_node); + + *element = dest_node; + + return res; +} + + +/** + * asn1_print_structure: + * @out: pointer to the output file (e.g. stdout). + * @structure: pointer to the structure that you want to visit. + * @name: an element of the structure + * @mode: specify how much of the structure to print, can be + * %ASN1_PRINT_NAME, %ASN1_PRINT_NAME_TYPE, + * %ASN1_PRINT_NAME_TYPE_VALUE, or %ASN1_PRINT_ALL. + * + * Prints on the @out file descriptor the structure's tree starting + * from the @name element inside the structure @structure. + **/ +void +asn1_print_structure (FILE * out, asn1_node_const structure, const char *name, + int mode) +{ + asn1_node_const p, root; + int k, indent = 0, len, len2, len3; + + if (out == NULL) + return; + + root = asn1_find_node (structure, name); + + if (root == NULL) + return; + + p = root; + while (p) + { + if (mode == ASN1_PRINT_ALL) + { + for (k = 0; k < indent; k++) + fprintf (out, " "); + fprintf (out, "name:"); + if (p->name[0] != 0) + fprintf (out, "%s ", p->name); + else + fprintf (out, "NULL "); + } + else + { + switch (type_field (p->type)) + { + case ASN1_ETYPE_CONSTANT: + case ASN1_ETYPE_TAG: + case ASN1_ETYPE_SIZE: + break; + default: + for (k = 0; k < indent; k++) + fprintf (out, " "); + fprintf (out, "name:"); + if (p->name[0] != 0) + fprintf (out, "%s ", p->name); + else + fprintf (out, "NULL "); + } + } + + if (mode != ASN1_PRINT_NAME) + { + unsigned type = type_field (p->type); + switch (type) + { + case ASN1_ETYPE_CONSTANT: + if (mode == ASN1_PRINT_ALL) + fprintf (out, "type:CONST"); + break; + case ASN1_ETYPE_TAG: + if (mode == ASN1_PRINT_ALL) + fprintf (out, "type:TAG"); + break; + case ASN1_ETYPE_SIZE: + if (mode == ASN1_PRINT_ALL) + fprintf (out, "type:SIZE"); + break; + case ASN1_ETYPE_DEFAULT: + fprintf (out, "type:DEFAULT"); + break; + case ASN1_ETYPE_IDENTIFIER: + fprintf (out, "type:IDENTIFIER"); + break; + case ASN1_ETYPE_ANY: + fprintf (out, "type:ANY"); + break; + case ASN1_ETYPE_CHOICE: + fprintf (out, "type:CHOICE"); + break; + case ASN1_ETYPE_DEFINITIONS: + fprintf (out, "type:DEFINITIONS"); + break; + CASE_HANDLED_ETYPES: + fprintf (out, "%s", _asn1_tags[type].desc); + break; + default: + break; + } + } + + if ((mode == ASN1_PRINT_NAME_TYPE_VALUE) || (mode == ASN1_PRINT_ALL)) + { + switch (type_field (p->type)) + { + case ASN1_ETYPE_CONSTANT: + if (mode == ASN1_PRINT_ALL) + if (p->value) + fprintf (out, " value:%s", p->value); + break; + case ASN1_ETYPE_TAG: + if (mode == ASN1_PRINT_ALL) + if (p->value) + fprintf (out, " value:%s", p->value); + break; + case ASN1_ETYPE_SIZE: + if (mode == ASN1_PRINT_ALL) + if (p->value) + fprintf (out, " value:%s", p->value); + break; + case ASN1_ETYPE_DEFAULT: + if (p->value) + fprintf (out, " value:%s", p->value); + else if (p->type & CONST_TRUE) + fprintf (out, " value:TRUE"); + else if (p->type & CONST_FALSE) + fprintf (out, " value:FALSE"); + break; + case ASN1_ETYPE_IDENTIFIER: + if (p->value) + fprintf (out, " value:%s", p->value); + break; + case ASN1_ETYPE_INTEGER: + if (p->value) + { + len2 = -1; + len = asn1_get_length_der (p->value, p->value_len, &len2); + fprintf (out, " value:0x"); + if (len > 0) + for (k = 0; k < len; k++) + fprintf (out, "%02x", (unsigned) (p->value)[k + len2]); + } + break; + case ASN1_ETYPE_ENUMERATED: + if (p->value) + { + len2 = -1; + len = asn1_get_length_der (p->value, p->value_len, &len2); + fprintf (out, " value:0x"); + if (len > 0) + for (k = 0; k < len; k++) + fprintf (out, "%02x", (unsigned) (p->value)[k + len2]); + } + break; + case ASN1_ETYPE_BOOLEAN: + if (p->value) + { + if (p->value[0] == 'T') + fprintf (out, " value:TRUE"); + else if (p->value[0] == 'F') + fprintf (out, " value:FALSE"); + } + break; + case ASN1_ETYPE_BIT_STRING: + if (p->value) + { + len2 = -1; + len = asn1_get_length_der (p->value, p->value_len, &len2); + if (len > 0) + { + fprintf (out, " value(%i):", + (len - 1) * 8 - (p->value[len2])); + for (k = 1; k < len; k++) + fprintf (out, "%02x", + (unsigned) (p->value)[k + len2]); + } + } + break; + case ASN1_ETYPE_GENERALIZED_TIME: + case ASN1_ETYPE_UTC_TIME: + if (p->value) + { + fprintf (out, " value:"); + for (k = 0; k < p->value_len; k++) + fprintf (out, "%c", (p->value)[k]); + } + break; + case ASN1_ETYPE_GENERALSTRING: + case ASN1_ETYPE_NUMERIC_STRING: + case ASN1_ETYPE_IA5_STRING: + case ASN1_ETYPE_TELETEX_STRING: + case ASN1_ETYPE_PRINTABLE_STRING: + case ASN1_ETYPE_UNIVERSAL_STRING: + case ASN1_ETYPE_UTF8_STRING: + case ASN1_ETYPE_VISIBLE_STRING: + if (p->value) + { + len2 = -1; + len = asn1_get_length_der (p->value, p->value_len, &len2); + fprintf (out, " value:"); + if (len > 0) + for (k = 0; k < len; k++) + fprintf (out, "%c", (p->value)[k + len2]); + } + break; + case ASN1_ETYPE_BMP_STRING: + case ASN1_ETYPE_OCTET_STRING: + if (p->value) + { + len2 = -1; + len = asn1_get_length_der (p->value, p->value_len, &len2); + fprintf (out, " value:"); + if (len > 0) + for (k = 0; k < len; k++) + fprintf (out, "%02x", (unsigned) (p->value)[k + len2]); + } + break; + case ASN1_ETYPE_OBJECT_ID: + if (p->value) + fprintf (out, " value:%s", p->value); + break; + case ASN1_ETYPE_ANY: + if (p->value) + { + len3 = -1; + len2 = asn1_get_length_der (p->value, p->value_len, &len3); + fprintf (out, " value:"); + if (len2 > 0) + for (k = 0; k < len2; k++) + fprintf (out, "%02x", (unsigned) (p->value)[k + len3]); + } + break; + case ASN1_ETYPE_SET: + case ASN1_ETYPE_SET_OF: + case ASN1_ETYPE_CHOICE: + case ASN1_ETYPE_DEFINITIONS: + case ASN1_ETYPE_SEQUENCE_OF: + case ASN1_ETYPE_SEQUENCE: + case ASN1_ETYPE_NULL: + break; + default: + break; + } + } + + if (mode == ASN1_PRINT_ALL) + { + if (p->type & 0x1FFFFF00) + { + fprintf (out, " attr:"); + if (p->type & CONST_UNIVERSAL) + fprintf (out, "UNIVERSAL,"); + if (p->type & CONST_PRIVATE) + fprintf (out, "PRIVATE,"); + if (p->type & CONST_APPLICATION) + fprintf (out, "APPLICATION,"); + if (p->type & CONST_EXPLICIT) + fprintf (out, "EXPLICIT,"); + if (p->type & CONST_IMPLICIT) + fprintf (out, "IMPLICIT,"); + if (p->type & CONST_TAG) + fprintf (out, "TAG,"); + if (p->type & CONST_DEFAULT) + fprintf (out, "DEFAULT,"); + if (p->type & CONST_TRUE) + fprintf (out, "TRUE,"); + if (p->type & CONST_FALSE) + fprintf (out, "FALSE,"); + if (p->type & CONST_LIST) + fprintf (out, "LIST,"); + if (p->type & CONST_MIN_MAX) + fprintf (out, "MIN_MAX,"); + if (p->type & CONST_OPTION) + fprintf (out, "OPTION,"); + if (p->type & CONST_1_PARAM) + fprintf (out, "1_PARAM,"); + if (p->type & CONST_SIZE) + fprintf (out, "SIZE,"); + if (p->type & CONST_DEFINED_BY) + fprintf (out, "DEF_BY,"); + if (p->type & CONST_GENERALIZED) + fprintf (out, "GENERALIZED,"); + if (p->type & CONST_UTC) + fprintf (out, "UTC,"); + if (p->type & CONST_SET) + fprintf (out, "SET,"); + if (p->type & CONST_NOT_USED) + fprintf (out, "NOT_USED,"); + if (p->type & CONST_ASSIGN) + fprintf (out, "ASSIGNMENT,"); + } + } + + if (mode == ASN1_PRINT_ALL) + { + fprintf (out, "\n"); + } + else + { + switch (type_field (p->type)) + { + case ASN1_ETYPE_CONSTANT: + case ASN1_ETYPE_TAG: + case ASN1_ETYPE_SIZE: + break; + default: + fprintf (out, "\n"); + } + } + + if (p->down) + { + p = p->down; + indent += 2; + } + else if (p == root) + { + p = NULL; + break; + } + else if (p->right) + p = p->right; + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == root) + { + p = NULL; + break; + } + indent -= 2; + if (p->right) + { + p = p->right; + break; + } + } + } + } +} + + + +/** + * asn1_number_of_elements: + * @element: pointer to the root of an ASN1 structure. + * @name: the name of a sub-structure of ROOT. + * @num: pointer to an integer where the result will be stored + * + * Counts the number of elements of a sub-structure called NAME with + * names equal to "?1","?2", ... + * + * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if + * @name is not known, %ASN1_GENERIC_ERROR if pointer @num is %NULL. + **/ +int +asn1_number_of_elements (asn1_node_const element, const char *name, int *num) +{ + asn1_node_const node, p; + + if (num == NULL) + return ASN1_GENERIC_ERROR; + + *num = 0; + + node = asn1_find_node (element, name); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node->down; + + while (p) + { + if (p->name[0] == '?') + (*num)++; + p = p->right; + } + + return ASN1_SUCCESS; +} + + +/** + * asn1_find_structure_from_oid: + * @definitions: ASN1 definitions + * @oidValue: value of the OID to search (e.g. "1.2.3.4"). + * + * Search the structure that is defined just after an OID definition. + * + * Returns: %NULL when @oidValue not found, otherwise the pointer to a + * constant string that contains the element name defined just after + * the OID. + **/ +const char * +asn1_find_structure_from_oid (asn1_node_const definitions, + const char *oidValue) +{ + char name[2 * ASN1_MAX_NAME_SIZE + 2]; + char value[ASN1_MAX_NAME_SIZE]; + asn1_node p; + int len; + int result; + const char *definitionsName; + + if ((definitions == NULL) || (oidValue == NULL)) + return NULL; /* ASN1_ELEMENT_NOT_FOUND; */ + + definitionsName = definitions->name; + + /* search the OBJECT_ID into definitions */ + p = definitions->down; + while (p) + { + if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) && + (p->type & CONST_ASSIGN)) + { + snprintf (name, sizeof (name), "%s.%s", definitionsName, p->name); + + len = ASN1_MAX_NAME_SIZE; + result = asn1_read_value (definitions, name, value, &len); + + if ((result == ASN1_SUCCESS) && (!strcmp (oidValue, value))) + { + p = p->right; + if (p == NULL) /* reach the end of ASN1 definitions */ + return NULL; /* ASN1_ELEMENT_NOT_FOUND; */ + + return p->name; + } + } + p = p->right; + } + + return NULL; /* ASN1_ELEMENT_NOT_FOUND; */ +} + +/** + * asn1_copy_node: + * @dst: Destination asn1 node. + * @dst_name: Field name in destination node. + * @src: Source asn1 node. + * @src_name: Field name in source node. + * + * Create a deep copy of a asn1_node variable. That + * function requires @dst to be expanded using asn1_create_element(). + * + * Returns: Return %ASN1_SUCCESS on success. + **/ +int +asn1_copy_node (asn1_node dst, const char *dst_name, + asn1_node_const src, const char *src_name) +{ + int result; + asn1_node dst_node; + void *data = NULL; + int size = 0; + + result = asn1_der_coding (src, src_name, NULL, &size, NULL); + if (result != ASN1_MEM_ERROR) + return result; + + data = malloc (size); + if (data == NULL) + return ASN1_MEM_ERROR; + + result = asn1_der_coding (src, src_name, data, &size, NULL); + if (result != ASN1_SUCCESS) + { + free (data); + return result; + } + + dst_node = asn1_find_node (dst, dst_name); + if (dst_node == NULL) + { + free (data); + return ASN1_ELEMENT_NOT_FOUND; + } + + result = asn1_der_decoding (&dst_node, data, size, NULL); + + free (data); + + return result; +} + +/** + * asn1_dup_node: + * @src: Source asn1 node. + * @src_name: Field name in source node. + * + * Create a deep copy of a asn1_node variable. This function + * will return an exact copy of the provided structure. + * + * Returns: Return %NULL on failure. + **/ +asn1_node +asn1_dup_node (asn1_node_const src, const char *src_name) +{ + return _asn1_copy_structure2 (src, src_name); +} diff --git a/grub-core/lib/libtasn1/lib/structure.h b/grub-core/lib/libtasn1/lib/structure.h new file mode 100644 index 000000000..b973ce963 --- /dev/null +++ b/grub-core/lib/libtasn1/lib/structure.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2002-2022 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/*************************************************/ +/* File: structure.h */ +/* Description: list of exported object by */ +/* "structure.c" */ +/*************************************************/ + +#ifndef _STRUCTURE_H +# define _STRUCTURE_H + +# include "parser_aux.h" /* list_type */ + +int _asn1_create_static_structure (asn1_node_const pointer, + char *output_file_name, char *vector_name); + +asn1_node _asn1_copy_structure3 (asn1_node_const source_node); + +asn1_node _asn1_add_single_node (unsigned int type); + +asn1_node _asn1_find_left (asn1_node_const node); + +int +_asn1_delete_structure (list_type * e_list, asn1_node * structure, + unsigned int flags); + +#endif diff --git a/grub-core/lib/libtasn1/libtasn1.h b/grub-core/lib/libtasn1/libtasn1.h new file mode 100644 index 000000000..51cc7879f --- /dev/null +++ b/grub-core/lib/libtasn1/libtasn1.h @@ -0,0 +1,643 @@ +/* + * Copyright (C) 2002-2022 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * LIBTASN1 is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * LIBTASN1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with LIBTASN1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +/** + * SECTION:libtasn1 + * @short_description: GNU ASN.1 library + * + * The Libtasn1 library provides Abstract Syntax Notation One (ASN.1, as + * specified by the X.680 ITU-T recommendation) parsing and structures + * management, and Distinguished Encoding Rules (DER, as per X.690) + * encoding and decoding functions. + */ + + +#ifndef LIBTASN1_H +# define LIBTASN1_H + +# ifndef ASN1_API +# if defined ASN1_BUILDING && defined HAVE_VISIBILITY && HAVE_VISIBILITY +# define ASN1_API __attribute__((__visibility__("default"))) +# elif defined ASN1_BUILDING && defined _MSC_VER && ! defined ASN1_STATIC +# define ASN1_API __declspec(dllexport) +# elif defined _MSC_VER && ! defined ASN1_STATIC +# define ASN1_API __declspec(dllimport) +# else +# define ASN1_API +# endif +# endif + +# ifdef __GNUC__ +# define __LIBTASN1_CONST__ __attribute__((const)) +# define __LIBTASN1_PURE__ __attribute__((pure)) +# else +# define __LIBTASN1_CONST__ +# define __LIBTASN1_PURE__ +# endif + +# include +# include +# include /* for FILE* */ + +# ifdef __cplusplus +extern "C" +{ +# endif + +/** + * ASN1_VERSION: + * + * Version of the library as a string. + */ +# define ASN1_VERSION "4.19.0" + +/** + * ASN1_VERSION_MAJOR: + * + * Major version number of the library. + */ +# define ASN1_VERSION_MAJOR 4 + +/** + * ASN1_VERSION_MINOR: + * + * Minor version number of the library. + */ +# define ASN1_VERSION_MINOR 19 + +/** + * ASN1_VERSION_PATCH: + * + * Patch version number of the library. + */ +# define ASN1_VERSION_PATCH 0 + +/** + * ASN1_VERSION_NUMBER: + * + * Version number of the library as a number. + */ +# define ASN1_VERSION_NUMBER 0x041300 + + +# if defined __GNUC__ && !defined ASN1_INTERNAL_BUILD +# define _ASN1_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +# if _ASN1_GCC_VERSION >= 30100 +# define _ASN1_GCC_ATTR_DEPRECATED __attribute__ ((__deprecated__)) +# endif +# endif + +# ifndef _ASN1_GCC_ATTR_DEPRECATED +# define _ASN1_GCC_ATTR_DEPRECATED +# endif + +/*****************************************/ +/* Errors returned by libtasn1 functions */ +/*****************************************/ +# define ASN1_SUCCESS 0 +# define ASN1_FILE_NOT_FOUND 1 +# define ASN1_ELEMENT_NOT_FOUND 2 +# define ASN1_IDENTIFIER_NOT_FOUND 3 +# define ASN1_DER_ERROR 4 +# define ASN1_VALUE_NOT_FOUND 5 +# define ASN1_GENERIC_ERROR 6 +# define ASN1_VALUE_NOT_VALID 7 +# define ASN1_TAG_ERROR 8 +# define ASN1_TAG_IMPLICIT 9 +# define ASN1_ERROR_TYPE_ANY 10 +# define ASN1_SYNTAX_ERROR 11 +# define ASN1_MEM_ERROR 12 +# define ASN1_MEM_ALLOC_ERROR 13 +# define ASN1_DER_OVERFLOW 14 +# define ASN1_NAME_TOO_LONG 15 +# define ASN1_ARRAY_ERROR 16 +# define ASN1_ELEMENT_NOT_EMPTY 17 +# define ASN1_TIME_ENCODING_ERROR 18 +# define ASN1_RECURSION 19 + +/*************************************/ +/* Constants used in asn1_visit_tree */ +/*************************************/ +# define ASN1_PRINT_NAME 1 +# define ASN1_PRINT_NAME_TYPE 2 +# define ASN1_PRINT_NAME_TYPE_VALUE 3 +# define ASN1_PRINT_ALL 4 + +/*****************************************/ +/* Constants returned by asn1_read_tag */ +/*****************************************/ +# define ASN1_CLASS_UNIVERSAL 0x00 /* old: 1 */ +# define ASN1_CLASS_APPLICATION 0x40 /* old: 2 */ +# define ASN1_CLASS_CONTEXT_SPECIFIC 0x80 /* old: 3 */ +# define ASN1_CLASS_PRIVATE 0xC0 /* old: 4 */ +# define ASN1_CLASS_STRUCTURED 0x20 + +/*****************************************/ +/* Constants returned by asn1_read_tag */ +/*****************************************/ +# define ASN1_TAG_BOOLEAN 0x01 +# define ASN1_TAG_INTEGER 0x02 +# define ASN1_TAG_SEQUENCE 0x10 +# define ASN1_TAG_SET 0x11 +# define ASN1_TAG_OCTET_STRING 0x04 +# define ASN1_TAG_BIT_STRING 0x03 +# define ASN1_TAG_UTCTime 0x17 +# define ASN1_TAG_GENERALIZEDTime 0x18 +# define ASN1_TAG_OBJECT_ID 0x06 +# define ASN1_TAG_ENUMERATED 0x0A +# define ASN1_TAG_NULL 0x05 +# define ASN1_TAG_GENERALSTRING 0x1B +# define ASN1_TAG_NUMERIC_STRING 0x12 +# define ASN1_TAG_IA5_STRING 0x16 +# define ASN1_TAG_TELETEX_STRING 0x14 +# define ASN1_TAG_PRINTABLE_STRING 0x13 +# define ASN1_TAG_UNIVERSAL_STRING 0x1C +# define ASN1_TAG_BMP_STRING 0x1E +# define ASN1_TAG_UTF8_STRING 0x0C +# define ASN1_TAG_VISIBLE_STRING 0x1A + +/** + * asn1_node: + * + * Structure definition used for the node of the tree + * that represents an ASN.1 DEFINITION. + */ + typedef struct asn1_node_st asn1_node_st; + + typedef asn1_node_st *asn1_node; + typedef const asn1_node_st *asn1_node_const; + +/** + * ASN1_MAX_NAME_SIZE: + * + * Maximum number of characters of a name + * inside a file with ASN1 definitions. + */ +# define ASN1_MAX_NAME_SIZE 64 + + +/** + * asn1_static_node: + * @name: Node name + * @type: Node typ + * @value: Node value + * + * For the on-disk format of ASN.1 trees, created by asn1_parser2array(). + */ + typedef struct asn1_static_node_st + { + const char *name; /* Node name */ + unsigned int type; /* Node type */ + const void *value; /* Node value */ + } asn1_static_node; + +/* List of constants for field type of asn1_static_node */ +# define ASN1_ETYPE_INVALID 0 +# define ASN1_ETYPE_CONSTANT 1 +# define ASN1_ETYPE_IDENTIFIER 2 +# define ASN1_ETYPE_INTEGER 3 +# define ASN1_ETYPE_BOOLEAN 4 +# define ASN1_ETYPE_SEQUENCE 5 +# define ASN1_ETYPE_BIT_STRING 6 +# define ASN1_ETYPE_OCTET_STRING 7 +# define ASN1_ETYPE_TAG 8 +# define ASN1_ETYPE_DEFAULT 9 +# define ASN1_ETYPE_SIZE 10 +# define ASN1_ETYPE_SEQUENCE_OF 11 +# define ASN1_ETYPE_OBJECT_ID 12 +# define ASN1_ETYPE_ANY 13 +# define ASN1_ETYPE_SET 14 +# define ASN1_ETYPE_SET_OF 15 +# define ASN1_ETYPE_DEFINITIONS 16 +# define ASN1_ETYPE_CHOICE 18 +# define ASN1_ETYPE_IMPORTS 19 +# define ASN1_ETYPE_NULL 20 +# define ASN1_ETYPE_ENUMERATED 21 +# define ASN1_ETYPE_GENERALSTRING 27 +# define ASN1_ETYPE_NUMERIC_STRING 28 +# define ASN1_ETYPE_IA5_STRING 29 +# define ASN1_ETYPE_TELETEX_STRING 30 +# define ASN1_ETYPE_PRINTABLE_STRING 31 +# define ASN1_ETYPE_UNIVERSAL_STRING 32 +# define ASN1_ETYPE_BMP_STRING 33 +# define ASN1_ETYPE_UTF8_STRING 34 +# define ASN1_ETYPE_VISIBLE_STRING 35 +# define ASN1_ETYPE_UTC_TIME 36 +# define ASN1_ETYPE_GENERALIZED_TIME 37 + +/** + * ASN1_DELETE_FLAG_ZEROIZE: + * + * Used by: asn1_delete_structure2() + * + * Zeroize values prior to deinitialization. + */ +# define ASN1_DELETE_FLAG_ZEROIZE 1 + +/** + * ASN1_DECODE_FLAG_ALLOW_PADDING: + * + * Used by: asn1_der_decoding2() + * + * This flag would allow arbitrary data past the DER data. + */ +# define ASN1_DECODE_FLAG_ALLOW_PADDING 1 +/** + * ASN1_DECODE_FLAG_STRICT_DER: + * + * Used by: asn1_der_decoding2() + * + * This flag would ensure that no BER decoding takes place. + */ +# define ASN1_DECODE_FLAG_STRICT_DER (1<<1) +/** + * ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME: + * + * Used by: asn1_der_decoding2() + * + * This flag will tolerate Time encoding errors when in strict DER. + */ +# define ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME (1<<2) + + /* *INDENT-OFF* */ + +/** + * asn1_data_node_st: + * @name: Node name + * @value: Node value + * @value_len: Node value size + * @type: Node value type (ASN1_ETYPE_*) + * + * Data node inside a #asn1_node structure. + */ + struct asn1_data_node_st + { + const char *name; /* Node name */ + const void *value; /* Node value */ + unsigned int value_len; /* Node value size */ + unsigned int type; /* Node value type (ASN1_ETYPE_*) */ +}; + + /* *INDENT-ON* */ + + typedef struct asn1_data_node_st asn1_data_node_st; + +/***********************************/ +/* Fixed constants */ +/***********************************/ + +/** + * ASN1_MAX_ERROR_DESCRIPTION_SIZE: + * + * Maximum number of characters + * of a description message + * (null character included). + */ +# define ASN1_MAX_ERROR_DESCRIPTION_SIZE 128 + +/***********************************/ +/* Functions definitions */ +/***********************************/ + + extern ASN1_API int + asn1_parser2tree (const char *file, + asn1_node * definitions, char *error_desc); + + extern ASN1_API int + asn1_parser2array (const char *inputFileName, + const char *outputFileName, + const char *vectorName, char *error_desc); + + extern ASN1_API int + asn1_array2tree (const asn1_static_node * array, + asn1_node * definitions, char *errorDescription); + + extern ASN1_API void + asn1_print_structure (FILE * out, asn1_node_const structure, + const char *name, int mode); + + extern ASN1_API int + asn1_create_element (asn1_node_const definitions, + const char *source_name, asn1_node * element); + + extern ASN1_API int asn1_delete_structure (asn1_node * structure); + + extern ASN1_API int asn1_delete_structure2 (asn1_node * structure, + unsigned int flags); + + extern ASN1_API int + asn1_delete_element (asn1_node structure, const char *element_name); + + extern ASN1_API int + asn1_write_value (asn1_node node_root, const char *name, + const void *ivalue, int len); + + extern ASN1_API int + asn1_read_value (asn1_node_const root, const char *name, + void *ivalue, int *len); + + extern ASN1_API int + asn1_read_value_type (asn1_node_const root, const char *name, + void *ivalue, int *len, unsigned int *etype); + + extern ASN1_API int + asn1_read_node_value (asn1_node_const node, asn1_data_node_st * data); + + extern ASN1_API int + asn1_number_of_elements (asn1_node_const element, const char *name, + int *num); + + extern ASN1_API int + asn1_der_coding (asn1_node_const element, const char *name, + void *ider, int *len, char *ErrorDescription); + + extern ASN1_API int + asn1_der_decoding2 (asn1_node * element, const void *ider, + int *max_ider_len, unsigned int flags, + char *errorDescription); + + extern ASN1_API int + asn1_der_decoding (asn1_node * element, const void *ider, + int ider_len, char *errorDescription); + +/* Do not use. Use asn1_der_decoding() instead. */ + extern ASN1_API int + asn1_der_decoding_element (asn1_node * structure, + const char *elementName, + const void *ider, int len, + char *errorDescription) + _ASN1_GCC_ATTR_DEPRECATED; + + extern ASN1_API int + asn1_der_decoding_startEnd (asn1_node element, + const void *ider, int ider_len, + const char *name_element, + int *start, int *end); + + extern ASN1_API int + asn1_expand_any_defined_by (asn1_node_const definitions, + asn1_node * element); + + extern ASN1_API int + asn1_expand_octet_string (asn1_node_const definitions, + asn1_node * element, + const char *octetName, const char *objectName); + + extern ASN1_API int + asn1_read_tag (asn1_node_const root, const char *name, + int *tagValue, int *classValue); + + extern ASN1_API const char *asn1_find_structure_from_oid (asn1_node_const + definitions, + const char + *oidValue); + + __LIBTASN1_PURE__ + extern ASN1_API const char *asn1_check_version (const char *req_version); + + __LIBTASN1_PURE__ extern ASN1_API const char *asn1_strerror (int error); + + extern ASN1_API void asn1_perror (int error); + +# define ASN1_MAX_TAG_SIZE 4 +# define ASN1_MAX_LENGTH_SIZE 9 +# define ASN1_MAX_TL_SIZE (ASN1_MAX_TAG_SIZE+ASN1_MAX_LENGTH_SIZE) + extern ASN1_API long + asn1_get_length_der (const unsigned char *der, int der_len, int *len); + + extern ASN1_API long + asn1_get_length_ber (const unsigned char *ber, int ber_len, int *len); + + extern ASN1_API void + asn1_length_der (unsigned long int len, unsigned char *der, int *der_len); + +/* Other utility functions. */ + + extern ASN1_API + int asn1_decode_simple_der (unsigned int etype, const unsigned char *der, + unsigned int _der_len, + const unsigned char **str, + unsigned int *str_len); + + extern ASN1_API + int asn1_decode_simple_ber (unsigned int etype, const unsigned char *der, + unsigned int _der_len, + unsigned char **str, + unsigned int *str_len, unsigned int *ber_len); + + extern ASN1_API int + asn1_encode_simple_der (unsigned int etype, const unsigned char *str, + unsigned int str_len, unsigned char *tl, + unsigned int *tl_len); + + extern ASN1_API asn1_node + asn1_find_node (asn1_node_const pointer, const char *name); + + extern ASN1_API int + asn1_copy_node (asn1_node dst, const char *dst_name, + asn1_node_const src, const char *src_name); + extern ASN1_API asn1_node + asn1_dup_node (asn1_node_const src, const char *src_name); + +/* Internal and low-level DER utility functions. */ + + extern ASN1_API int + asn1_get_tag_der (const unsigned char *der, int der_len, + unsigned char *cls, int *len, unsigned long *tag); + + extern ASN1_API void + asn1_octet_der (const unsigned char *str, int str_len, + unsigned char *der, int *der_len); + + extern ASN1_API int + asn1_get_octet_der (const unsigned char *der, int der_len, + int *ret_len, unsigned char *str, + int str_size, int *str_len); + + extern ASN1_API void asn1_bit_der (const unsigned char *str, int bit_len, + unsigned char *der, int *der_len); + + extern ASN1_API int + asn1_get_bit_der (const unsigned char *der, int der_len, + int *ret_len, unsigned char *str, + int str_size, int *bit_len); + + extern ASN1_API int + asn1_get_object_id_der (const unsigned char *der, + int der_len, int *ret_len, + char *str, int str_size); + + extern ASN1_API int + asn1_object_id_der (const char *str, unsigned char *der, int *der_len, + unsigned flags); + +/* Compatibility types */ + +/** + * asn1_retCode: + * + * Type formerly returned by libtasn1 functions. + * + * Deprecated: 3.0: Use int instead. + */ + typedef int asn1_retCode _ASN1_GCC_ATTR_DEPRECATED; + +/** + * node_asn_struct: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_node instead. + */ +# ifndef ASN1_DISABLE_DEPRECATED +# if _ASN1_GCC_VERSION >= 30100 +# define node_asn_struct _Pragma ("GCC warning \"'node_asn_struct' macro is deprecated, use 'asn1_node' instead.\"") asn1_node_st +# else +# define node_asn_struct asn1_node_st +# endif +# endif /* !ASN1_DISABLE_DEPRECATED */ + +/** + * node_asn: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_node instead. + */ +# ifndef ASN1_DISABLE_DEPRECATED +# if _ASN1_GCC_VERSION >= 30100 +# define node_asn _Pragma ("GCC warning \"'node_asn' macro is deprecated, use 'asn1_node' instead.\"") asn1_node_st +# else +# define node_asn asn1_node_st +# endif +# endif /* !ASN1_DISABLE_DEPRECATED */ + +/** + * ASN1_TYPE: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_node instead. + */ +# ifndef ASN1_DISABLE_DEPRECATED +# if _ASN1_GCC_VERSION >= 30100 +# define ASN1_TYPE _Pragma ("GCC warning \"'ASN1_TYPE' macro is deprecated, use 'asn1_node' instead.\"") asn1_node +# else +# define ASN1_TYPE asn1_node +# endif +# endif /* !ASN1_DISABLE_DEPRECATED */ + +/** + * ASN1_TYPE_EMPTY: + * + * Compat #define. + * + * Deprecated: 3.0: Use NULL instead. + */ +# ifndef ASN1_DISABLE_DEPRECATED +# if _ASN1_GCC_VERSION >= 30100 +# define ASN1_TYPE_EMPTY _Pragma ("GCC warning \"'ASN1_TYPE_EMPTY' macro is deprecated, use 'NULL' instead.\"") NULL +# else +# define ASN1_TYPE_EMPTY NULL +# endif +# endif /* !ASN1_DISABLE_DEPRECATED */ + +/** + * static_struct_asn: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_static_node instead. + */ +# ifndef ASN1_DISABLE_DEPRECATED +# if _ASN1_GCC_VERSION >= 30100 +# define static_struct_asn _Pragma ("GCC warning \"'static_struct_asn' macro is deprecated, use 'asn1_static_node_st' instead.\"") asn1_static_node_st +# else +# define static_struct_asn asn1_static_node_st +# endif +# endif /* !ASN1_DISABLE_DEPRECATED */ + +/** + * ASN1_ARRAY_TYPE: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_static_node instead. + */ +# ifndef ASN1_DISABLE_DEPRECATED +# if _ASN1_GCC_VERSION >= 30100 +# define ASN1_ARRAY_TYPE _Pragma ("GCC warning \"'ASN1_ARRAY_TYPE' macro is deprecated, use 'asn1_static_node' instead.\"") asn1_static_node +# else +# define ASN1_ARRAY_TYPE asn1_static_node +# endif +# endif /* !ASN1_DISABLE_DEPRECATED */ + +/** + * asn1_static_node_t: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_static_node instead. + */ +# ifndef ASN1_DISABLE_DEPRECATED +# if _ASN1_GCC_VERSION >= 30100 +# define asn1_static_node_t _Pragma ("GCC warning \"'asn1_static_node_t' macro is deprecated, use 'asn1_static_node' instead.\"") asn1_static_node +# else +# define asn1_static_node_t asn1_static_node +# endif +# endif /* !ASN1_DISABLE_DEPRECATED */ + +/** + * node_data_struct: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_data_node_st instead. + */ +# ifndef ASN1_DISABLE_DEPRECATED +# if _ASN1_GCC_VERSION >= 30100 +# define node_data_struct _Pragma ("GCC warning \"'node_data_struct' macro is deprecated, use 'asn1_data_node_st' instead.\"") asn1_data_node_st +# else +# define node_data_struct asn1_data_node_st +# endif +# endif /* !ASN1_DISABLE_DEPRECATED */ + +/** + * ASN1_DATA_NODE: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_data_node_st instead. + */ +# ifndef ASN1_DISABLE_DEPRECATED +# if _ASN1_GCC_VERSION >= 30100 +# define ASN1_DATA_NODE _Pragma ("GCC warning \"'asn1_static_node_t' macro is deprecated, use 'asn1_static_node' instead.\"") asn1_data_node_st +# else +# define ASN1_DATA_NODE asn1_data_node_st +# endif +# endif /* !ASN1_DISABLE_DEPRECATED */ + +# ifdef __cplusplus +} +# endif + +#endif /* LIBTASN1_H */ diff --git a/grub-core/lib/libtasn1/tests/CVE-2018-1000654-1_asn1_tab.h b/grub-core/lib/libtasn1/tests/CVE-2018-1000654-1_asn1_tab.h new file mode 100644 index 000000000..e7930136c --- /dev/null +++ b/grub-core/lib/libtasn1/tests/CVE-2018-1000654-1_asn1_tab.h @@ -0,0 +1,32 @@ +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +const asn1_static_node CVE_2018_1000654_1_asn1_tab[] = { + { "TEST_TREE", 536875024, NULL }, + { NULL, 1610612748, NULL }, + { "iso", 1073741825, "1"}, + { "identified-organization", 1073741825, "3"}, + { "dod", 1073741825, "6"}, + { "internet", 1073741825, "1"}, + { "security", 1073741825, "5"}, + { "mechanisms", 1073741825, "5"}, + { "pkix", 1073741825, "7"}, + { "id-mod", 1073741825, "0"}, + { "id-pkix1-implicit-88", 1, "2"}, + { "id-xnyTest", 1879048204, NULL }, + { NULL, 1073741825, "id-ix"}, + { NULL, 1073741825, "29"}, + { NULL, 1, "1"}, + { "id-ix", 1880096780, "OBJECR"}, + { NULL, 1073741825, "id-ix"}, + { NULL, 1073741825, "29"}, + { NULL, 1, "2"}, + { "id-xnyTest", 805306380, NULL }, + { NULL, 1073741825, "id-ix"}, + { NULL, 1073741825, "29"}, + { NULL, 1, "1"}, + { NULL, 0, NULL } +}; diff --git a/grub-core/lib/libtasn1/tests/CVE-2018-1000654-2_asn1_tab.h b/grub-core/lib/libtasn1/tests/CVE-2018-1000654-2_asn1_tab.h new file mode 100644 index 000000000..e8170f5d5 --- /dev/null +++ b/grub-core/lib/libtasn1/tests/CVE-2018-1000654-2_asn1_tab.h @@ -0,0 +1,36 @@ +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +const asn1_static_node CVE_2018_1000654_2_asn1_tab[] = { + { "TEST_TREE", 536875024, NULL }, + { NULL, 1610612748, NULL }, + { "iso", 1073741825, "1"}, + { "identified-organization", 1073741825, "3"}, + { "dod", 1073741825, "6"}, + { "internet", 1073741825, "1"}, + { "security", 1073741825, "5"}, + { "mechanisms", 1073741825, "5"}, + { "pkix", 1073741825, "7"}, + { "id-mod", 1073741825, "0"}, + { "id-pkix1-implicit-88", 1, "2"}, + { "id-oneTest", 1879048204, NULL }, + { NULL, 1073741825, "id-two"}, + { NULL, 1073741825, "9"}, + { NULL, 1, "1"}, + { "id-two", 1879048204, NULL }, + { NULL, 1073741825, "id-three"}, + { NULL, 1073741825, "2"}, + { NULL, 1, "2"}, + { "id-three", 1879048204, NULL }, + { NULL, 1073741825, "id-four"}, + { NULL, 1073741825, "3"}, + { NULL, 1, "3"}, + { "id-four", 805306380, NULL }, + { NULL, 1073741825, "id-two"}, + { NULL, 1073741825, "3"}, + { NULL, 1, "3"}, + { NULL, 0, NULL } +}; diff --git a/grub-core/lib/libtasn1/tests/CVE-2018-1000654.c b/grub-core/lib/libtasn1/tests/CVE-2018-1000654.c new file mode 100644 index 000000000..0c22b7012 --- /dev/null +++ b/grub-core/lib/libtasn1/tests/CVE-2018-1000654.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2002-2022 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * 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 . + * + */ + +/****************************************************************/ +/* Description: reproducer for CVE-2018-1000654 */ +/****************************************************************/ + +#include +#include +#include +#include "CVE-2018-1000654-1_asn1_tab.h" +#include "CVE-2018-1000654-2_asn1_tab.h" + +int +main (int argc, char *argv[]) +{ + int result, verbose = 0; + asn1_node definitions = NULL; + char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + + if (argc > 1) + verbose = 1; + + printf ("Test 1\n"); + + result = + asn1_array2tree (CVE_2018_1000654_1_asn1_tab, &definitions, + errorDescription); + if (result != ASN1_RECURSION) + { + asn1_perror (result); + printf ("ErrorDescription = %s\n\n", errorDescription); + exit (1); + } + + asn1_delete_structure (&definitions); + + printf ("Test 2\n"); + + result = + asn1_array2tree (CVE_2018_1000654_2_asn1_tab, &definitions, + errorDescription); + if (result != ASN1_RECURSION) + { + asn1_perror (result); + printf ("ErrorDescription = %s\n\n", errorDescription); + exit (1); + } + + asn1_delete_structure (&definitions); + + if (verbose) + printf ("Success\n"); + exit (0); +} diff --git a/grub-core/lib/libtasn1/tests/Test_overflow.c b/grub-core/lib/libtasn1/tests/Test_overflow.c new file mode 100644 index 000000000..c61dea4bb --- /dev/null +++ b/grub-core/lib/libtasn1/tests/Test_overflow.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2012-2022 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * 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 . + * + */ + +/* Written by Simon Josefsson */ + +#include +#include +#include +#include + +#include "libtasn1.h" + +int +main (int argc, char **argv) +{ + /* Test that values larger than long are rejected. This has worked + fine with all versions of libtasn1. */ + int verbose = 0; + + if (argc > 1) + verbose = 1; + + { + unsigned char der[] = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"; + long l; + int len; + + l = asn1_get_length_der (der, sizeof der, &len); + + if (l == -2L) + { + if (verbose) + puts ("OK: asn1_get_length_der bignum"); + } + else + { + printf ("ERROR: asn1_get_length_der bignum (l %ld len %d)\n", l, len); + return 1; + } + } + + /* Test that values larger than int but smaller than long are + rejected. This limitation was introduced with libtasn1 2.12. */ + if (LONG_MAX > INT_MAX) + { + unsigned long num = ((long) UINT_MAX) << 2; + unsigned char der[20]; + int der_len; + long l; + int len; + + asn1_length_der (num, der, &der_len); + + l = asn1_get_length_der (der, der_len, &len); + + if (l == -2L) + { + if (verbose) + puts ("OK: asn1_get_length_der intnum"); + } + else + { + printf ("ERROR: asn1_get_length_der intnum (l %ld len %d)\n", l, + len); + return 1; + } + } + + /* Test that values larger than would fit in the input string are + rejected. This problem was fixed in libtasn1 2.12. */ + { + unsigned long num = 64; + unsigned char der[20]; + int der_len; + long l; + int len; + + asn1_length_der (num, der, &der_len); + + der_len = sizeof (der); + l = asn1_get_length_der (der, der_len, &len); + + if (l == -4L) + { + if (verbose) + puts ("OK: asn1_get_length_der overflow-small"); + } + else + { + printf ("ERROR: asn1_get_length_der overflow-small (l %ld len %d)\n", + l, len); + return 1; + } + } + + /* Test that values larger than would fit in the input string are + rejected. This problem was fixed in libtasn1 2.12. */ + { + unsigned long num = 1073741824; + unsigned char der[20]; + int der_len; + long l; + int len; + + asn1_length_der (num, der, &der_len); + + der_len = sizeof (der); + l = asn1_get_length_der (der, der_len, &len); + + if (l == -4L) + { + if (verbose) + puts ("OK: asn1_get_length_der overflow-large1"); + } + else + { + printf ("ERROR: asn1_get_length_der overflow-large1 (l %ld len %d)\n", + l, len); + return 1; + } + } + + /* Test that values larger than would fit in the input string are + rejected. This problem was fixed in libtasn1 2.12. */ + { + unsigned long num = 2147483649; + unsigned char der[20]; + int der_len; + long l; + int len; + + asn1_length_der (num, der, &der_len); + + der_len = sizeof (der); + l = asn1_get_length_der (der, der_len, &len); + + if (l == -2L) + { + if (verbose) + puts ("OK: asn1_get_length_der overflow-large2"); + } + else + { + printf ("ERROR: asn1_get_length_der overflow-large2 (l %ld len %d)\n", + l, len); + return 1; + } + } + + return 0; +} diff --git a/grub-core/lib/libtasn1/tests/Test_simple.c b/grub-core/lib/libtasn1/tests/Test_simple.c new file mode 100644 index 000000000..6cd07e069 --- /dev/null +++ b/grub-core/lib/libtasn1/tests/Test_simple.c @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2011-2022 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * 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 . + * + * Written by Simon Josefsson + * + */ + +#include +#include +#include + +#include "libtasn1.h" + +struct tv +{ + int bitlen; + const char *bitstr; + int derlen; + const char *der; +}; + +static const struct tv tv[] = { + {0, "", 2, "\x01\x00"}, + {1, "\x00", 3, "\x02\x07\x00"}, + {2, "\x00", 3, "\x02\x06\x00"}, + {3, "\x00", 3, "\x02\x05\x00"}, + {4, "\x00", 3, "\x02\x04\x00"}, + {5, "\x00", 3, "\x02\x03\x00"}, + {6, "\x00", 3, "\x02\x02\x00"}, + {7, "\x00", 3, "\x02\x01\x00"}, + {8, "\x00\x00", 3, "\x02\x00\x00"}, + {9, "\x00\x00", 4, "\x03\x07\x00\x00"}, + {10, "\x00\x00", 4, "\x03\x06\x00\x00"}, + {11, "\x00\x00", 4, "\x03\x05\x00\x00"}, + {12, "\x00\x00", 4, "\x03\x04\x00\x00"}, + {13, "\x00\x00", 4, "\x03\x03\x00\x00"}, + {14, "\x00\x00", 4, "\x03\x02\x00\x00"}, + {15, "\x00\x00", 4, "\x03\x01\x00\x00"}, + {16, "\x00\x00", 4, "\x03\x00\x00\x00"}, + {17, "\x00\x00\x00", 5, "\x04\x07\x00\x00\x00"}, + {18, "\x00\x00\x00", 5, "\x04\x06\x00\x00\x00"}, + {19, "\x00\x00\x00", 5, "\x04\x05\x00\x00\x00"}, + {1, "\xFF", 3, "\x02\x07\x80"}, + {2, "\xFF", 3, "\x02\x06\xc0"}, + {3, "\xFF", 3, "\x02\x05\xe0"}, + {4, "\xFF", 3, "\x02\x04\xf0"}, + {5, "\xFF", 3, "\x02\x03\xf8"}, + {6, "\xFF", 3, "\x02\x02\xfc"}, + {7, "\xFF", 3, "\x02\x01\xfe"}, + {8, "\xFF\xFF", 3, "\x02\x00\xff"}, + {9, "\xFF\xFF", 4, "\x03\x07\xff\x80"}, + {10, "\xFF\xFF", 4, "\x03\x06\xff\xc0"}, + {11, "\xFF\xFF", 4, "\x03\x05\xff\xe0"}, + {12, "\xFF\xFF", 4, "\x03\x04\xff\xf0"}, + {13, "\xFF\xFF", 4, "\x03\x03\xff\xf8"}, + {14, "\xFF\xFF", 4, "\x03\x02\xff\xfc"}, + {15, "\xFF\xFF", 4, "\x03\x01\xff\xfe"}, + {16, "\xFF\xFF", 4, "\x03\x00\xff\xff"}, + {17, "\xFF\xFF\xFF", 5, "\x04\x07\xff\xff\x80"}, + {18, "\xFF\xFF\xFF", 5, "\x04\x06\xff\xff\xc0"}, + {19, "\xFF\xFF\xFF", 5, "\x04\x05\xff\xff\xe0"}, +}; + +int +main (int argc, char *argv[]) +{ + int result; + unsigned char der[100]; + unsigned char str[100]; + int der_len = sizeof (der); + int str_size = sizeof (str); + int ret_len, bit_len; + size_t i; + + { + unsigned int etype = 38; + unsigned int my_str_len = 10; + unsigned char my_str[10]; + unsigned int tl_len = 10; + unsigned char tl[10]; + + /* https://gitlab.com/gnutls/libtasn1/-/issues/32 */ + result = asn1_encode_simple_der (etype, my_str, my_str_len, tl, &tl_len); + if (result != ASN1_VALUE_NOT_VALID) + { + fprintf (stderr, "asn1_encode_simple_der out of range etype\n"); + return 1; + } + } + + /* Dummy test */ + + asn1_bit_der (NULL, 0, der, &der_len); + result = asn1_get_bit_der (der, 0, &ret_len, str, str_size, &bit_len); + if (result != ASN1_GENERIC_ERROR) + { + fprintf (stderr, "asn1_get_bit_der zero\n"); + return 1; + } + + /* Encode short strings with increasing bit lengths */ + + for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) + { + /* Encode */ + + asn1_bit_der ((const unsigned char *) tv[i].bitstr, tv[i].bitlen, + der, &der_len); + +#if 0 + { + size_t j; + for (j = 0; j < der_len; j++) + printf ("\\x%02x", der[j]); + printf ("\n"); + } +#endif + + if (der_len != tv[i].derlen || memcmp (der, tv[i].der, der_len) != 0) + { + fprintf (stderr, "asn1_bit_der iter %lu\n", (unsigned long) i); + return 1; + } + + /* Decode it */ + + result = asn1_get_bit_der (der, der_len, &ret_len, str, + str_size, &bit_len); + if (result != ASN1_SUCCESS || ret_len != tv[i].derlen + || bit_len != tv[i].bitlen) + { + fprintf (stderr, "asn1_get_bit_der iter %lu, err: %d\n", + (unsigned long) i, result); + return 1; + } + } + + + /* Decode sample from "A Layman's Guide to a Subset of ASN.1, BER, + and DER" section 5.4 "BIT STRING": "The BER encoding of the BIT + STRING value "011011100101110111" can be any of the following, + among others, depending on the choice of padding bits, the form + of length octets [...]". + */ + + /* 03 04 06 6e 5d c0 DER encoding */ + + memcpy (der, "\x04\x06\x6e\x5d\xc0", 5); + der_len = 5; + + result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len); + if (result != ASN1_SUCCESS || ret_len != 5 + || bit_len != 18 || memcmp (str, "\x6e\x5d\xc0", 3) != 0) + { + fprintf (stderr, "asn1_get_bit_der example\n"); + return 1; + } + + der_len = sizeof (der); + asn1_bit_der (str, bit_len, der, &der_len); + if (der_len != 5 || memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0) + { + fprintf (stderr, "asn1_bit_der example roundtrip\n"); + return 1; + } + + /* 03 04 06 6e 5d e0 padded with "100000" */ + + memcpy (der, "\x04\x06\x6e\x5d\xe0", 5); + der_len = 5; + + result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len); + if (result != ASN1_SUCCESS || ret_len != 5 + || bit_len != 18 || memcmp (str, "\x6e\x5d\xe0", 3) != 0) + { + fprintf (stderr, "asn1_get_bit_der example padded\n"); + return 1; + } + + der_len = sizeof (der); + asn1_bit_der (str, bit_len, der, &der_len); + if (der_len != 5 || memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0) + { + fprintf (stderr, "asn1_bit_der example roundtrip\n"); + return 1; + } + + /* 03 81 04 06 6e 5d c0 long form of length octets */ + + memcpy (der, "\x81\x04\x06\x6e\x5d\xc0", 6); + der_len = 6; + + result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len); + + if (result != ASN1_SUCCESS || ret_len != 6 + || bit_len != 18 || memcmp (str, "\x6e\x5d\xc0", 3) != 0) + { + fprintf (stderr, "asn1_get_bit_der example long form\n"); + return 1; + } + + der_len = sizeof (der); + asn1_bit_der (str, bit_len, der, &der_len); + if (der_len != 5 || memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0) + { + fprintf (stderr, "asn1_bit_der example roundtrip\n"); + return 1; + } + + return 0; +} diff --git a/grub-core/lib/libtasn1/tests/Test_strings.c b/grub-core/lib/libtasn1/tests/Test_strings.c new file mode 100644 index 000000000..27f7215e1 --- /dev/null +++ b/grub-core/lib/libtasn1/tests/Test_strings.c @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2012-2022 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * 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 . + * + * Written by Simon Josefsson + * + */ + +#include +#include +#include + +#include "libtasn1.h" + +struct tv +{ + unsigned int etype; + unsigned int str_len; + const void *str; + unsigned int der_len; + const void *der; +}; + +static const struct tv tv[] = { + {ASN1_ETYPE_IA5_STRING, 20, + "\x63\x73\x63\x61\x40\x70\x61\x73\x73\x70\x6f\x72\x74\x2e\x67\x6f\x76\x2e\x67\x72", + 22, + "\x16\x14\x63\x73\x63\x61\x40\x70\x61\x73\x73\x70\x6f\x72\x74\x2e\x67\x6f\x76\x2e\x67\x72"}, + {ASN1_ETYPE_PRINTABLE_STRING, 5, "\x4e\x69\x6b\x6f\x73", + 7, "\x13\x05\x4e\x69\x6b\x6f\x73"}, + {ASN1_ETYPE_UTF8_STRING, 12, "ΑττÎčÎșÎź", + 14, "\x0c\x0c\xce\x91\xcf\x84\xcf\x84\xce\xb9\xce\xba\xce\xae"}, + {ASN1_ETYPE_TELETEX_STRING, 15, + "\x53\x69\x6d\x6f\x6e\x20\x4a\x6f\x73\x65\x66\x73\x73\x6f\x6e", + 17, + "\x14\x0f\x53\x69\x6d\x6f\x6e\x20\x4a\x6f\x73\x65\x66\x73\x73\x6f\x6e"}, + {ASN1_ETYPE_OCTET_STRING, 36, + "\x30\x22\x80\x0F\x32\x30\x31\x31\x30\x38\x32\x31\x30\x38\x30\x30\x30\x36\x5A\x81\x0F\x32\x30\x31\x31\x30\x38\x32\x33\x32\x30\x35\x39\x35\x39\x5A", + 38, + "\x04\x24\x30\x22\x80\x0F\x32\x30\x31\x31\x30\x38\x32\x31\x30\x38\x30\x30\x30\x36\x5A\x81\x0F\x32\x30\x31\x31\x30\x38\x32\x33\x32\x30\x35\x39\x35\x39\x5A"} +}; + +#define SSTR(x) sizeof(x)-1,x +static const struct tv ber[] = { + {ASN1_ETYPE_OCTET_STRING, + SSTR ("\xa0\xa0"), + SSTR ("\x24\x80\x04\x82\x00\x02\xa0\xa0\x00\x00")}, + {ASN1_ETYPE_OCTET_STRING, + SSTR ("\xa0\xa0\xb0\xb0\xb0"), + SSTR + ("\x24\x80\x04\x82\x00\x02\xa0\xa0\x04\x82\x00\x03\xb0\xb0\xb0\x00\x00")}, + {ASN1_ETYPE_OCTET_STRING, + SSTR ("\xa0\xa0\xb0\xb0\xb0\xa1\xa1"), + SSTR + ("\x24\x80\x04\x82\x00\x02\xa0\xa0\x04\x82\x00\x03\xb0\xb0\xb0\x24\x80\x04\x82\x00\x02\xa1\xa1\x00\x00\x00\x00")}, + {ASN1_ETYPE_OCTET_STRING, + SSTR ("\xa0\xa0\xb0\xb0\xb0\xa1\xa1\xc1"), + SSTR + ("\x24\x80\x04\x82\x00\x02\xa0\xa0\x04\x82\x00\x03\xb0\xb0\xb0\x24\x80\x04\x82\x00\x02\xa1\xa1\x04\x82\x00\x01\xc1\x00\x00\x00\x00")}, +}; + +int +main (int argc, char *argv[]) +{ + int ret; + unsigned char tl[ASN1_MAX_TL_SIZE]; + unsigned int tl_len, der_len, str_len; + const unsigned char *str; + unsigned char *b; + unsigned int i; + + /* Dummy test */ + + for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) + { + /* Encode */ + tl_len = sizeof (tl); + ret = asn1_encode_simple_der (tv[i].etype, tv[i].str, tv[i].str_len, + tl, &tl_len); + if (ret != ASN1_SUCCESS) + { + fprintf (stderr, "Encoding error in %u: %s\n", i, + asn1_strerror (ret)); + return 1; + } + der_len = tl_len + tv[i].str_len; + + if (der_len != tv[i].der_len || memcmp (tl, tv[i].der, tl_len) != 0) + { + fprintf (stderr, + "DER encoding differs in %u! (size: %u, expected: %u)\n", + i, der_len, tv[i].der_len); + return 1; + } + + /* decoding */ + ret = + asn1_decode_simple_der (tv[i].etype, tv[i].der, tv[i].der_len, &str, + &str_len); + if (ret != ASN1_SUCCESS) + { + fprintf (stderr, "Decoding error in %u: %s\n", i, + asn1_strerror (ret)); + return 1; + } + + if (str_len != tv[i].str_len || memcmp (str, tv[i].str, str_len) != 0) + { + fprintf (stderr, + "DER decoded data differ in %u! (size: %u, expected: %u)\n", + i, der_len, tv[i].str_len); + return 1; + } + } + + /* BER decoding */ + for (i = 0; i < sizeof (ber) / sizeof (ber[0]); i++) + { + /* decoding */ + ret = + asn1_decode_simple_ber (ber[i].etype, ber[i].der, ber[i].der_len, &b, + &str_len, NULL); + if (ret != ASN1_SUCCESS) + { + fprintf (stderr, "BER decoding error in %u: %s\n", i, + asn1_strerror (ret)); + return 1; + } + + if (str_len != ber[i].str_len || memcmp (b, ber[i].str, str_len) != 0) + { + fprintf (stderr, + "BER decoded data differ in %u! (size: %u, expected: %u)\n", + i, str_len, ber[i].str_len); + return 1; + } + free (b); + } + + + return 0; +} diff --git a/grub-core/lib/libtasn1/tests/object-id-decoding.c b/grub-core/lib/libtasn1/tests/object-id-decoding.c new file mode 100644 index 000000000..06a6c52a2 --- /dev/null +++ b/grub-core/lib/libtasn1/tests/object-id-decoding.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. + * + * This file is part of LIBTASN1. + * + * 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 . + * + */ + +#include +#include +#include + +#include "libtasn1.h" + +struct tv +{ + int der_len; + const unsigned char *der; + const char *oid; + int expected_error; +}; + +static const struct tv tv[] = { + {.der_len = 5, + .der = (void *) "\x06\x03\x80\x37\x03", + .oid = "2.999.3", + .expected_error = ASN1_DER_ERROR /* leading 0x80 */ + }, + {.der_len = 12, + .der = (void *) "\x06\x0a\x2b\x06\x01\x80\x01\x92\x08\x09\x05\x01", + .oid = "1.3.6.1.4.1.2312.9.5.1", + .expected_error = ASN1_DER_ERROR /* leading 0x80 */ + }, + {.der_len = 6, + .der = (void *) "\x06\x04\x01\x02\x03\x04", + .oid = "0.1.2.3.4", + .expected_error = ASN1_SUCCESS}, + {.der_len = 5, + .der = (void *) "\x06\x03\x51\x02\x03", + .oid = "2.1.2.3", + .expected_error = ASN1_SUCCESS}, + {.der_len = 5, + .der = (void *) "\x06\x03\x88\x37\x03", + .oid = "2.999.3", + .expected_error = ASN1_SUCCESS}, + {.der_len = 12, + .der = (void *) "\x06\x0a\x2b\x06\x01\x04\x01\x92\x08\x09\x05\x01", + .oid = "1.3.6.1.4.1.2312.9.5.1", + .expected_error = ASN1_SUCCESS}, + {.der_len = 19, + .der = + (void *) + "\x06\x11\xfa\x80\x00\x00\x00\x0e\x01\x0e\xfa\x80\x00\x00\x00\x0e\x63\x6f\x6d", + .oid = "2.1998768.0.0.14.1.14.1998848.0.0.14.99.111.109", + .expected_error = ASN1_SUCCESS}, + {.der_len = 19, + .der = + (void *) + "\x06\x11\x2b\x06\x01\x04\x01\x92\x08\x09\x02\xaa\xda\xbe\xbe\xfa\x72\x01\x07", + .oid = "1.3.6.1.4.1.2312.9.2.1467399257458.1.7", + .expected_error = ASN1_SUCCESS}, +}; + +int +main (int argc, char *argv[]) +{ + char str[128]; + int ret, ret_len; + size_t i; + + for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) + { + /* decode */ + ret = + asn1_get_object_id_der (tv[i].der + 1, + tv[i].der_len - 1, &ret_len, str, + sizeof (str)); + if (ret != tv[i].expected_error) + { + fprintf (stderr, + "%d: asn1_get_object_id_der iter %lu: got '%s' expected %d\n", + __LINE__, (unsigned long) i, asn1_strerror (ret), + tv[i].expected_error); + return 1; + } + + if (tv[i].expected_error != ASN1_SUCCESS) + continue; + + if (ret_len != tv[i].der_len - 1) + { + fprintf (stderr, + "%d: iter %lu: error in DER, length returned is %d, had %d\n", + __LINE__, (unsigned long) i, ret_len, tv[i].der_len - 1); + return 1; + } + + if (strcmp (tv[i].oid, str) != 0) + { + fprintf (stderr, + "%d: strcmp iter %lu: got invalid OID: %s, expected: %s\n", + __LINE__, (unsigned long) i, str, tv[i].oid); + return 1; + } + + } + + return 0; +} diff --git a/grub-core/lib/libtasn1/tests/object-id-encoding.c b/grub-core/lib/libtasn1/tests/object-id-encoding.c new file mode 100644 index 000000000..1a3396986 --- /dev/null +++ b/grub-core/lib/libtasn1/tests/object-id-encoding.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2019 Nikos Mavrogiannopoulos + * + * This file is part of LIBTASN1. + * + * 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 . + * + */ + +#include +#include +#include + +#include "libtasn1.h" + +struct tv +{ + int der_len; + const unsigned char *der; + const char *oid; + int expected_error; +}; + +static const struct tv tv[] = { + {.der_len = 0, + .der = (void *) "", + .oid = "5.999.3", + .expected_error = ASN1_VALUE_NOT_VALID /* cannot start with 5 */ + }, + {.der_len = 0, + .der = (void *) "", + .oid = "0.48.9", + .expected_error = ASN1_VALUE_NOT_VALID /* second field cannot be 48 */ + }, + {.der_len = 0, + .der = (void *) "", + .oid = "1.40.9", + .expected_error = ASN1_VALUE_NOT_VALID /* second field cannot be 40 */ + }, + {.der_len = 4, + .der = (void *) "\x06\x02\x4f\x63", + .oid = "1.39.99", + .expected_error = ASN1_SUCCESS, + }, + {.der_len = 6, + .der = (void *) "\x06\x04\x01\x02\x03\x04", + .oid = "0.1.2.3.4", + .expected_error = ASN1_SUCCESS}, + {.der_len = 5, + .der = (void *) "\x06\x03\x51\x02\x03", + .oid = "2.1.2.3", + .expected_error = ASN1_SUCCESS}, + {.der_len = 5, + .der = (void *) "\x06\x03\x88\x37\x03", + .oid = "2.999.3", + .expected_error = ASN1_SUCCESS}, + {.der_len = 12, + .der = (void *) "\x06\x0a\x2b\x06\x01\x04\x01\x92\x08\x09\x05\x01", + .oid = "1.3.6.1.4.1.2312.9.5.1", + .expected_error = ASN1_SUCCESS}, + {.der_len = 19, + .der = + (void *) + "\x06\x11\xfa\x80\x00\x00\x00\x0e\x01\x0e\xfa\x80\x00\x00\x00\x0e\x63\x6f\x6d", + .oid = "2.1998768.0.0.14.1.14.1998848.0.0.14.99.111.109", + .expected_error = ASN1_SUCCESS}, + {.der_len = 19, + .der = + (void *) + "\x06\x11\x2b\x06\x01\x04\x01\x92\x08\x09\x02\xaa\xda\xbe\xbe\xfa\x72\x01\x07", + .oid = "1.3.6.1.4.1.2312.9.2.1467399257458.1.7", + .expected_error = ASN1_SUCCESS}, +}; + +int +main (int argc, char *argv[]) +{ + unsigned char der[128]; + int ret, der_len, i, j; + + for (i = 0; i < (int) (sizeof (tv) / sizeof (tv[0])); i++) + { + der_len = sizeof (der); + ret = asn1_object_id_der (tv[i].oid, der, &der_len, 0); + if (ret != ASN1_SUCCESS) + { + if (ret == tv[i].expected_error) + continue; + fprintf (stderr, + "%d: iter %lu, encoding of OID failed: %s\n", + __LINE__, (unsigned long) i, asn1_strerror (ret)); + return 1; + } + else if (ret != tv[i].expected_error) + { + fprintf (stderr, + "%d: iter %lu, encoding of OID %s succeeded when expecting failure\n", + __LINE__, (unsigned long) i, tv[i].oid); + return 1; + } + + if (der_len != tv[i].der_len || memcmp (der, tv[i].der, der_len) != 0) + { + fprintf (stderr, + "%d: iter %lu, re-encoding of OID %s resulted to different string (%d vs %d bytes)\n", + __LINE__, (unsigned long) i, tv[i].oid, der_len, + tv[i].der_len); + fprintf (stderr, "\nGot:\t\t"); + for (j = 0; j < der_len; j++) + fprintf (stderr, "%.2x", der[j]); + + fprintf (stderr, "\nExpected:\t"); + for (j = 0; j < tv[i].der_len; j++) + fprintf (stderr, "%.2x", tv[i].der[j]); + fprintf (stderr, "\n"); + + return 1; + } + } + + return 0; +} diff --git a/grub-core/lib/libtasn1/tests/octet-string.c b/grub-core/lib/libtasn1/tests/octet-string.c new file mode 100644 index 000000000..69eb18a62 --- /dev/null +++ b/grub-core/lib/libtasn1/tests/octet-string.c @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2011-2022 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * 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 . + * + * Written by Simon Josefsson and Nikos Mavrogiannopoulos + * + */ + +#include +#include +#include + +#include "libtasn1.h" + +struct tv +{ + const char *name; + int der_len; + const unsigned char *der_str; + int len; + const unsigned char *string; + int expected_error; + int ber; +}; + +static const struct tv tv[] = { + {.name = "primitive octet strings", + .der_len = 10, + .der_str = (void *) "\x04\x08\x01\x23\x45\x67\x89\xab\xcd\xef", + .len = 8, + .string = (void *) "\x01\x23\x45\x67\x89\xab\xcd\xef", + .ber = 0}, + {.der_len = 22, + .der_str = + (void *) + "\x04\x14\x13\x00\xd9\xa8\x47\xf7\xf2\x1c\xf4\xb0\xec\x5f\xc1\x73\xe5\x1b\x25\xc2\x62\x27", + .len = 20, + .string = + (void *) + "\x13\x00\xD9\xA8\x47\xF7\xF2\x1C\xF4\xB0\xEC\x5F\xC1\x73\xE5\x1B\x25\xC2\x62\x27"}, + + {.name = "long type of length", + .der_len = 23, + .der_str = + (void *) + "\x04\x81\x14\x13\x00\xd9\xa8\x47\xf7\xf2\x1c\xf4\xb0\xec\x5f\xc1\x73\xe5\x1b\x25\xc2\x62\x27", + .len = 20, + .string = + (void *) + "\x13\x00\xD9\xA8\x47\xF7\xF2\x1C\xF4\xB0\xEC\x5F\xC1\x73\xE5\x1B\x25\xC2\x62\x27", + .ber = 1}, + {.der_len = 11, + .der_str = (void *) "\x04\x81\x08\x01\x23\x45\x67\x89\xab\xcd\xef", + .len = 8, + .string = (void *) "\x01\x23\x45\x67\x89\xab\xcd\xef", + .ber = 1}, + + {.name = "constructed - indefinite", + .der_len = 11, + .der_str = (void *) "\x24\x80\x04\x05\x01\x02\x03\x04\x05\x00\x00", + .len = 5, + .string = (void *) "\x01\x02\x03\x04\x05", + .ber = 1, + }, + + {.name = "constructed - definite - concat", + .der_len = 12, + .der_str = (void *) "\x24\x0a\x04\x04\x0a\x0b\x0c\x0d\x04\x02\x0e\x0f", + .len = 6, + .string = (void *) "\x0a\x0b\x0c\x0d\x0e\x0f", + .ber = 1, + }, + {.name = "constructed - definite - recursive", + .der_len = 15, + .der_str = + (void *) "\x24\x0d\x04\x04\x0a\x0b\x0c\x0d\x24\x05\x04\x00\x04\x01\x0f", + .len = 5, + .string = (void *) "\x0a\x0b\x0c\x0d\x0f", + .ber = 1, + }, + {.name = "constructed - definite - single", + .der_len = 7, + .der_str = (void *) "\x24\x05\x04\x03\x01\x02\x03", + .len = 3, + .string = (void *) "\x01\x02\x03", + .ber = 1, + }, + + /* a large amount of recursive indefinite encoding */ + {.name = "a large amount of recursive indefinite encoding", + .der_len = 29325, + .der_str = + (void *) + "\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80", + .len = 0, + .ber = 1, + .expected_error = ASN1_DER_ERROR} +}; + +int +main (int argc, char *argv[]) +{ + unsigned char str[100]; + unsigned char der[100]; + int der_len = sizeof (der); + int str_size = sizeof (str); + unsigned char *tmp = NULL; + int ret, ret_len, j; + size_t i; + + for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) + { + /* Decode */ + + if (tv[i].ber == 0) + { + str_size = sizeof (str); + ret = + asn1_get_octet_der (tv[i].der_str + 1, + tv[i].der_len - 1, &ret_len, str, + sizeof (str), &str_size); + if (ret != tv[i].expected_error) + { + fprintf (stderr, + "%d: asn1_get_octet_der: %s: got %d expected %d\n", + __LINE__, tv[i].name, ret, tv[i].expected_error); + return 1; + } + if (tv[i].expected_error) + continue; + + if (ret_len != tv[i].der_len - 1) + { + fprintf (stderr, + "%d: error in DER, length returned is %d, had %d\n", + __LINE__, ret_len, tv[i].der_len - 1); + return 1; + } + + if (str_size != tv[i].len + || memcmp (tv[i].string, str, tv[i].len) != 0) + { + fprintf (stderr, + "%d: memcmp: %s: got invalid decoding\n", + __LINE__, tv[i].name); + + fprintf (stderr, "\nGot:\t\t"); + for (j = 0; j < str_size; j++) + fprintf (stderr, "%.2x", str[j]); + + fprintf (stderr, "\nExpected:\t"); + for (j = 0; j < tv[i].len; j++) + fprintf (stderr, "%.2x", tv[i].string[j]); + fprintf (stderr, "\n"); + return 1; + } + + /* Encode */ + der_len = sizeof (der); + asn1_octet_der (str, str_size, der, &der_len); + + if (der_len != tv[i].der_len - 1 + || memcmp (tv[i].der_str + 1, der, tv[i].der_len - 1) != 0) + { + fprintf (stderr, + "encoding: %s: got invalid encoding\n", tv[i].name); + return 1; + } + } + + ret = + asn1_decode_simple_ber (ASN1_ETYPE_OCTET_STRING, + tv[i].der_str, tv[i].der_len, + &tmp, (unsigned int *) &str_size, + (unsigned int *) &der_len); + if (ret != tv[i].expected_error) + { + fprintf (stderr, + "%d: asn1_decode_simple_ber: %s: got %s expected %s\n", + __LINE__, tv[i].name, asn1_strerror (ret), + asn1_strerror (tv[i].expected_error)); + return 1; + } + if (tv[i].expected_error) + continue; + + if (der_len != tv[i].der_len) + { + fprintf (stderr, + "%d: error: %s: DER, length returned is %d, had %d\n", + __LINE__, tv[i].name, der_len, tv[i].der_len); + return 1; + } + + if (str_size != tv[i].len || memcmp (tv[i].string, tmp, tv[i].len) != 0) + { + fprintf (stderr, + "%d: memcmp: %s: got invalid decoding\n", + __LINE__, tv[i].name); + fprintf (stderr, "\nGot:\t\t"); + for (j = 0; j < str_size; j++) + fprintf (stderr, "%.2x", tmp[j]); + + fprintf (stderr, "\nExpected:\t"); + for (j = 0; j < tv[i].len; j++) + fprintf (stderr, "%.2x", tv[i].string[j]); + fprintf (stderr, "\n"); + return 1; + } + free (tmp); + tmp = NULL; + + } + + return 0; +} diff --git a/grub-core/lib/libtasn1/tests/reproducers.c b/grub-core/lib/libtasn1/tests/reproducers.c new file mode 100644 index 000000000..a09d8b021 --- /dev/null +++ b/grub-core/lib/libtasn1/tests/reproducers.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2019-2022 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * 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 . + * + */ + +/****************************************************************/ +/* Description: run reproducers for several fixed issues */ +/****************************************************************/ + +#include +#include +#include + +#include + +#include + +/* produces endless loop (fixed by d4b624b2): + * The following translates into a single node with all pointers + * (right,left,down) set to NULL. */ +const asn1_static_node endless_asn1_tab[] = { + {"TEST_TREE", 536875024, NULL}, + {NULL, 0, NULL} +}; + +/* produces memory leak (fixed by f16d1ff9): + * 152 bytes in 1 blocks are definitely lost in loss record 1 of 1 + * at 0x4837B65: calloc (vg_replace_malloc.c:762) + * by 0x4851C0D: _asn1_add_static_node (parser_aux.c:71) + * by 0x4853AAC: asn1_array2tree (structure.c:200) + * by 0x10923B: main (single_node.c:67) + */ +const asn1_static_node tab[] = { + {"a", CONST_DOWN, ""}, + {"b", 0, ""}, + {"c", 0, ""}, + {NULL, 0, NULL} +}; + +int +main (int argc, char *argv[]) +{ + int result, verbose = 0; + asn1_node definitions = NULL; + char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + + if (argc > 1) + verbose = 1; + + result = asn1_array2tree (endless_asn1_tab, &definitions, errorDescription); + if (result != ASN1_SUCCESS) + { + asn1_perror (result); + printf ("ErrorDescription = %s\n\n", errorDescription); + exit (EXIT_FAILURE); + } + + asn1_delete_structure (&definitions); + + definitions = NULL; + result = asn1_array2tree (tab, &definitions, errorDescription); + if (result != ASN1_SUCCESS) + { + asn1_perror (result); + printf ("ErrorDescription = %s\n\n", errorDescription); + exit (EXIT_FAILURE); + } + + asn1_delete_structure (&definitions); + + if (verbose) + printf ("Success\n"); + + exit (EXIT_SUCCESS); +} From fa498af7b999957da990f5216ff3cee3326dfbdf Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:34:31 +0800 Subject: [PATCH 212/402] libtasn1: Disable code not needed in GRUB We don't expect to be able to write ASN.1, only read it, so we can disable some code. Do that with #if 0/#endif, rather than deletion. This means that the difference between upstream and GRUB is smaller, which should make updating libtasn1 easier in the future. With these exclusions we also avoid the need for minmax.h, which is convenient because it means we don't have to import it from gnulib. Signed-off-by: Daniel Axtens Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- ...asn1-disable-code-not-needed-in-grub.patch | 320 ++++++++++++++++++ 1 file changed, 320 insertions(+) create mode 100644 grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch diff --git a/grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch b/grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch new file mode 100644 index 000000000..85bfa0732 --- /dev/null +++ b/grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch @@ -0,0 +1,320 @@ +From f534b3c0902adf207b7e51b8364284fe6a06731b Mon Sep 17 00:00:00 2001 +From: Daniel Axtens +Date: Fri, 1 May 2020 17:12:23 +1000 +Subject: [PATCH 01/13] libtasn1: disable code not needed in grub + +We don't expect to be able to write ASN.1, only read it, +so we can disable some code. + +Do that with #if 0/#endif, rather than deletion. This means +that the difference between upstream and grub is smaller, +which should make updating libtasn1 easier in the future. + +With these exclusions we also avoid the need for minmax.h, +which is convenient because it means we don't have to +import it from gnulib. + +Signed-off-by: Daniel Axtens +Signed-off-by: Gary Lin +Reviewed-by: Daniel Kiper +--- + grub-core/lib/libtasn1-grub/lib/coding.c | 12 ++++++++++-- + grub-core/lib/libtasn1-grub/lib/decoding.c | 2 ++ + grub-core/lib/libtasn1-grub/lib/element.c | 6 +++--- + grub-core/lib/libtasn1-grub/lib/errors.c | 3 +++ + grub-core/lib/libtasn1-grub/lib/structure.c | 10 ++++++---- + grub-core/lib/libtasn1-grub/libtasn1.h | 15 +++++++++++++++ + 6 files changed, 39 insertions(+), 9 deletions(-) + +diff --git a/grub-core/lib/libtasn1-grub/lib/coding.c b/grub-core/lib/libtasn1-grub/lib/coding.c +index ea5bc370e..5d03bca9d 100644 +--- a/grub-core/lib/libtasn1-grub/lib/coding.c ++++ b/grub-core/lib/libtasn1-grub/lib/coding.c +@@ -30,11 +30,11 @@ + #include "parser_aux.h" + #include + #include "element.h" +-#include "minmax.h" + #include + + #define MAX_TAG_LEN 16 + ++#if 0 /* GRUB SKIPPED IMPORTING */ + /******************************************************/ + /* Function : _asn1_error_description_value_not_found */ + /* Description: creates the ErrorDescription string */ +@@ -58,6 +58,7 @@ _asn1_error_description_value_not_found (asn1_node node, + Estrcat (ErrorDescription, "' not found"); + + } ++#endif + + /** + * asn1_length_der: +@@ -244,6 +245,7 @@ asn1_encode_simple_der (unsigned int etype, const unsigned char *str, + return ASN1_SUCCESS; + } + ++#if 0 /* GRUB SKIPPED IMPORTING */ + /******************************************************/ + /* Function : _asn1_time_der */ + /* Description: creates the DER coding for a TIME */ +@@ -278,7 +280,7 @@ _asn1_time_der (unsigned char *str, int str_len, unsigned char *der, + + return ASN1_SUCCESS; + } +- ++#endif + + /* + void +@@ -519,6 +521,7 @@ asn1_bit_der (const unsigned char *str, int bit_len, + } + + ++#if 0 /* GRUB SKIPPED IMPORTING */ + /******************************************************/ + /* Function : _asn1_complete_explicit_tag */ + /* Description: add the length coding to the EXPLICIT */ +@@ -595,6 +598,7 @@ _asn1_complete_explicit_tag (asn1_node node, unsigned char *der, + + return ASN1_SUCCESS; + } ++#endif + + const tag_and_class_st _asn1_tags[] = { + [ASN1_ETYPE_GENERALSTRING] = +@@ -647,6 +651,8 @@ const tag_and_class_st _asn1_tags[] = { + + unsigned int _asn1_tags_size = sizeof (_asn1_tags) / sizeof (_asn1_tags[0]); + ++ ++#if 0 /* GRUB SKIPPED IMPORTING */ + /******************************************************/ + /* Function : _asn1_insert_tag_der */ + /* Description: creates the DER coding of tags of one */ +@@ -1423,3 +1429,5 @@ error: + asn1_delete_structure (&node); + return err; + } ++ ++#endif +diff --git a/grub-core/lib/libtasn1-grub/lib/decoding.c b/grub-core/lib/libtasn1-grub/lib/decoding.c +index b9245c486..bf9cb13ac 100644 +--- a/grub-core/lib/libtasn1-grub/lib/decoding.c ++++ b/grub-core/lib/libtasn1-grub/lib/decoding.c +@@ -1620,6 +1620,7 @@ asn1_der_decoding (asn1_node * element, const void *ider, int ider_len, + return asn1_der_decoding2 (element, ider, &ider_len, 0, errorDescription); + } + ++#if 0 /* GRUB SKIPPED IMPORTING */ + /** + * asn1_der_decoding_element: + * @structure: pointer to an ASN1 structure +@@ -1650,6 +1651,7 @@ asn1_der_decoding_element (asn1_node * structure, const char *elementName, + { + return asn1_der_decoding (structure, ider, len, errorDescription); + } ++#endif + + /** + * asn1_der_decoding_startEnd: +diff --git a/grub-core/lib/libtasn1-grub/lib/element.c b/grub-core/lib/libtasn1-grub/lib/element.c +index d4c558e10..bc4c3c8d7 100644 +--- a/grub-core/lib/libtasn1-grub/lib/element.c ++++ b/grub-core/lib/libtasn1-grub/lib/element.c +@@ -118,7 +118,7 @@ _asn1_convert_integer (const unsigned char *value, unsigned char *value_out, + value_out[k2 - k] = val[k2]; + } + +-#if 0 ++#if 0 /* GRUB SKIPPED IMPORTING */ + printf ("_asn1_convert_integer: valueIn=%s, lenOut=%d", value, *len); + for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT; k++) + printf (", vOut[%d]=%d", k, value_out[k]); +@@ -191,7 +191,7 @@ _asn1_append_sequence_set (asn1_node node, struct node_tail_cache_st *pcache) + return ASN1_SUCCESS; + } + +- ++#if 0 + /** + * asn1_write_value: + * @node_root: pointer to a structure +@@ -646,7 +646,7 @@ asn1_write_value (asn1_node node_root, const char *name, + + return ASN1_SUCCESS; + } +- ++#endif + + #define PUT_VALUE( ptr, ptr_size, data, data_size) \ + *len = data_size; \ +diff --git a/grub-core/lib/libtasn1-grub/lib/errors.c b/grub-core/lib/libtasn1-grub/lib/errors.c +index aef5dfe6f..2b2322152 100644 +--- a/grub-core/lib/libtasn1-grub/lib/errors.c ++++ b/grub-core/lib/libtasn1-grub/lib/errors.c +@@ -57,6 +57,8 @@ static const libtasn1_error_entry error_algorithms[] = { + {0, 0} + }; + ++ ++#if 0 /* GRUB SKIPPED IMPORTING */ + /** + * asn1_perror: + * @error: is an error returned by a libtasn1 function. +@@ -73,6 +75,7 @@ asn1_perror (int error) + const char *str = asn1_strerror (error); + fprintf (stderr, "LIBTASN1 ERROR: %s\n", str ? str : "(null)"); + } ++#endif + + /** + * asn1_strerror: +diff --git a/grub-core/lib/libtasn1-grub/lib/structure.c b/grub-core/lib/libtasn1-grub/lib/structure.c +index 512dd601f..f5a947d57 100644 +--- a/grub-core/lib/libtasn1-grub/lib/structure.c ++++ b/grub-core/lib/libtasn1-grub/lib/structure.c +@@ -76,7 +76,7 @@ _asn1_find_left (asn1_node_const node) + return node->left; + } + +- ++#if 0 /* GRUB SKIPPED IMPORTING */ + int + _asn1_create_static_structure (asn1_node_const pointer, + char *output_file_name, char *vector_name) +@@ -155,7 +155,7 @@ _asn1_create_static_structure (asn1_node_const pointer, + + return ASN1_SUCCESS; + } +- ++#endif + + /** + * asn1_array2tree: +@@ -721,7 +721,7 @@ asn1_create_element (asn1_node_const definitions, const char *source_name, + return res; + } + +- ++#if 0 /* GRUB SKIPPED IMPORTING */ + /** + * asn1_print_structure: + * @out: pointer to the output file (e.g. stdout). +@@ -1062,7 +1062,7 @@ asn1_print_structure (FILE * out, asn1_node_const structure, const char *name, + } + } + } +- ++#endif + + + /** +@@ -1158,6 +1158,7 @@ asn1_find_structure_from_oid (asn1_node_const definitions, + return NULL; /* ASN1_ELEMENT_NOT_FOUND; */ + } + ++#if 0 /* GRUB SKIPPED IMPORTING */ + /** + * asn1_copy_node: + * @dst: Destination asn1 node. +@@ -1207,6 +1208,7 @@ asn1_copy_node (asn1_node dst, const char *dst_name, + + return result; + } ++#endif + + /** + * asn1_dup_node: +diff --git a/grub-core/lib/libtasn1-grub/libtasn1.h b/grub-core/lib/libtasn1-grub/libtasn1.h +index 51cc7879f..058ab27b0 100644 +--- a/grub-core/lib/libtasn1-grub/libtasn1.h ++++ b/grub-core/lib/libtasn1-grub/libtasn1.h +@@ -318,6 +318,8 @@ extern "C" + /* Functions definitions */ + /***********************************/ + ++/* These functions are not used in grub and should not be referenced. */ ++# if 0 /* GRUB SKIPPED IMPORTING */ + extern ASN1_API int + asn1_parser2tree (const char *file, + asn1_node * definitions, char *error_desc); +@@ -326,14 +328,17 @@ extern "C" + asn1_parser2array (const char *inputFileName, + const char *outputFileName, + const char *vectorName, char *error_desc); ++# endif + + extern ASN1_API int + asn1_array2tree (const asn1_static_node * array, + asn1_node * definitions, char *errorDescription); + ++# if 0 /* GRUB SKIPPED IMPORTING */ + extern ASN1_API void + asn1_print_structure (FILE * out, asn1_node_const structure, + const char *name, int mode); ++# endif + + extern ASN1_API int + asn1_create_element (asn1_node_const definitions, +@@ -347,9 +352,11 @@ extern "C" + extern ASN1_API int + asn1_delete_element (asn1_node structure, const char *element_name); + ++# if 0 /* GRUB SKIPPED IMPORTING */ + extern ASN1_API int + asn1_write_value (asn1_node node_root, const char *name, + const void *ivalue, int len); ++# endif + + extern ASN1_API int + asn1_read_value (asn1_node_const root, const char *name, +@@ -366,9 +373,11 @@ extern "C" + asn1_number_of_elements (asn1_node_const element, const char *name, + int *num); + ++# if 0 /* GRUB SKIPPED IMPORTING */ + extern ASN1_API int + asn1_der_coding (asn1_node_const element, const char *name, + void *ider, int *len, char *ErrorDescription); ++# endif + + extern ASN1_API int + asn1_der_decoding2 (asn1_node * element, const void *ider, +@@ -379,6 +388,7 @@ extern "C" + asn1_der_decoding (asn1_node * element, const void *ider, + int ider_len, char *errorDescription); + ++# if 0 /* GRUB SKIPPED IMPORTING */ + /* Do not use. Use asn1_der_decoding() instead. */ + extern ASN1_API int + asn1_der_decoding_element (asn1_node * structure, +@@ -386,6 +396,7 @@ extern "C" + const void *ider, int len, + char *errorDescription) + _ASN1_GCC_ATTR_DEPRECATED; ++# endif + + extern ASN1_API int + asn1_der_decoding_startEnd (asn1_node element, +@@ -411,12 +422,16 @@ extern "C" + const char + *oidValue); + ++# if 0 /* GRUB SKIPPED IMPORTING */ + __LIBTASN1_PURE__ + extern ASN1_API const char *asn1_check_version (const char *req_version); ++# endif + + __LIBTASN1_PURE__ extern ASN1_API const char *asn1_strerror (int error); + ++# if 0 /* GRUB SKIPPED IMPORTING */ + extern ASN1_API void asn1_perror (int error); ++# endif + + # define ASN1_MAX_TAG_SIZE 4 + # define ASN1_MAX_LENGTH_SIZE 9 +-- +2.43.0 + From 32fdfe60087bb757734d90db37e610c954c75d8b Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:34:32 +0800 Subject: [PATCH 213/402] libtasn1: Replace strcat() with strcpy() in _asn1_str_cat() strcat() is not available in GRUB. This commit replaces strcat() with strcpy() in _asn1_str_cat() as the preparation to replace other strcat() with the bounds-checking _asn1_str_cat(). Signed-off-by: Daniel Axtens Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- ...-strcat-with-strcpy-in-_asn1_str_cat.patch | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 grub-core/lib/libtasn1-patches/0002-libtasn1-replace-strcat-with-strcpy-in-_asn1_str_cat.patch diff --git a/grub-core/lib/libtasn1-patches/0002-libtasn1-replace-strcat-with-strcpy-in-_asn1_str_cat.patch b/grub-core/lib/libtasn1-patches/0002-libtasn1-replace-strcat-with-strcpy-in-_asn1_str_cat.patch new file mode 100644 index 000000000..908016df7 --- /dev/null +++ b/grub-core/lib/libtasn1-patches/0002-libtasn1-replace-strcat-with-strcpy-in-_asn1_str_cat.patch @@ -0,0 +1,33 @@ +From 93453558fbe34634096770933b3dc40c9199dfb4 Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Tue, 20 Aug 2024 16:14:51 +0800 +Subject: [PATCH 02/13] libtasn1: replace strcat() with strcpy() in + _asn1_str_cat() + +strcat() is not available in GRUB. This commit replaces strcat() with +strcpy() in _asn1_str_cat() as the preparation to replace other strcat() +with the bounds-checking _asn1_str_cat(). + +Signed-off-by: Daniel Axtens +Signed-off-by: Gary Lin +Reviewed-by: Daniel Kiper +--- + grub-core/lib/libtasn1-grub/lib/gstr.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/grub-core/lib/libtasn1-grub/lib/gstr.c b/grub-core/lib/libtasn1-grub/lib/gstr.c +index eef419554..a9c16f5d3 100644 +--- a/grub-core/lib/libtasn1-grub/lib/gstr.c ++++ b/grub-core/lib/libtasn1-grub/lib/gstr.c +@@ -36,7 +36,7 @@ _asn1_str_cat (char *dest, size_t dest_tot_size, const char *src) + + if (dest_tot_size - dest_size > str_size) + { +- strcat (dest, src); ++ strcpy (dest + dest_size, src); + } + else + { +-- +2.43.0 + From d86df91cbe2694ab9dbddad04fc0b0bacb2e208f Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:34:33 +0800 Subject: [PATCH 214/402] libtasn1: Replace strcat() with _asn1_str_cat() strcat() is not available in GRUB. This commit replaces strcat() and _asn1_strcat() with the bounds-checking _asn1_str_cat(). Signed-off-by: Daniel Axtens Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- ...n1-replace-strcat-with-_asn1_str_cat.patch | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 grub-core/lib/libtasn1-patches/0003-libtasn1-replace-strcat-with-_asn1_str_cat.patch diff --git a/grub-core/lib/libtasn1-patches/0003-libtasn1-replace-strcat-with-_asn1_str_cat.patch b/grub-core/lib/libtasn1-patches/0003-libtasn1-replace-strcat-with-_asn1_str_cat.patch new file mode 100644 index 000000000..d92ae1586 --- /dev/null +++ b/grub-core/lib/libtasn1-patches/0003-libtasn1-replace-strcat-with-_asn1_str_cat.patch @@ -0,0 +1,71 @@ +From b5efa707619fde1921a3cbd83f4ee95a7d995fa0 Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Tue, 20 Aug 2024 16:26:45 +0800 +Subject: [PATCH 03/13] libtasn1: replace strcat() with _asn1_str_cat() + +strcat() is not available in GRUB. This commit replaces strcat() and +_asn1_strcat() with the bounds-checking _asn1_str_cat(). + +Signed-off-by: Daniel Axtens +Signed-off-by: Gary Lin +Reviewed-by: Daniel Kiper +--- + grub-core/lib/libtasn1-grub/lib/decoding.c | 8 ++++---- + grub-core/lib/libtasn1-grub/lib/element.c | 2 +- + grub-core/lib/libtasn1-grub/lib/int.h | 1 - + 3 files changed, 5 insertions(+), 6 deletions(-) + +diff --git a/grub-core/lib/libtasn1-grub/lib/decoding.c b/grub-core/lib/libtasn1-grub/lib/decoding.c +index bf9cb13ac..51859fe36 100644 +--- a/grub-core/lib/libtasn1-grub/lib/decoding.c ++++ b/grub-core/lib/libtasn1-grub/lib/decoding.c +@@ -2016,8 +2016,8 @@ asn1_expand_octet_string (asn1_node_const definitions, asn1_node * element, + (p2->type & CONST_ASSIGN)) + { + strcpy (name, definitions->name); +- strcat (name, "."); +- strcat (name, p2->name); ++ _asn1_str_cat (name, sizeof (name), "."); ++ _asn1_str_cat (name, sizeof (name), p2->name); + + len = sizeof (value); + result = asn1_read_value (definitions, name, value, &len); +@@ -2034,8 +2034,8 @@ asn1_expand_octet_string (asn1_node_const definitions, asn1_node * element, + if (p2) + { + strcpy (name, definitions->name); +- strcat (name, "."); +- strcat (name, p2->name); ++ _asn1_str_cat (name, sizeof (name), "."); ++ _asn1_str_cat (name, sizeof (name), p2->name); + + result = asn1_create_element (definitions, name, &aux); + if (result == ASN1_SUCCESS) +diff --git a/grub-core/lib/libtasn1-grub/lib/element.c b/grub-core/lib/libtasn1-grub/lib/element.c +index bc4c3c8d7..8694fecb9 100644 +--- a/grub-core/lib/libtasn1-grub/lib/element.c ++++ b/grub-core/lib/libtasn1-grub/lib/element.c +@@ -688,7 +688,7 @@ asn1_write_value (asn1_node node_root, const char *name, + return ASN1_MEM_ERROR; \ + } else { \ + /* this strcat is checked */ \ +- if (ptr) _asn1_strcat (ptr, data); \ ++ if (ptr) _asn1_str_cat ((char *)ptr, ptr_size, (const char *)data); \ + } + + /** +diff --git a/grub-core/lib/libtasn1-grub/lib/int.h b/grub-core/lib/libtasn1-grub/lib/int.h +index d94d51c8c..cadd80df6 100644 +--- a/grub-core/lib/libtasn1-grub/lib/int.h ++++ b/grub-core/lib/libtasn1-grub/lib/int.h +@@ -115,7 +115,6 @@ extern const tag_and_class_st _asn1_tags[]; + # define _asn1_strtoul(n,e,b) strtoul((const char *) n, e, b) + # define _asn1_strcmp(a,b) strcmp((const char *)a, (const char *)b) + # define _asn1_strcpy(a,b) strcpy((char *)a, (const char *)b) +-# define _asn1_strcat(a,b) strcat((char *)a, (const char *)b) + + # if SIZEOF_UNSIGNED_LONG_INT == 8 + # define _asn1_strtou64(n,e,b) strtoul((const char *) n, e, b) +-- +2.43.0 + From 8f56e5e5cfa7a6e4fe94fad951f8e2dd738cff14 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:34:34 +0800 Subject: [PATCH 215/402] libtasn1: Adjust the header paths in libtasn1.h Since libtasn1.h is the header to be included by users, including the standard POSIX headers in libtasn1.h would force the user to add the CFLAGS/CPPFLAGS for the POSIX headers. This commit adjusts the header paths to use the grub headers instead of the standard POSIX headers, so that users only need to include libtasn1.h to use libtasn1 functions. Signed-off-by: Daniel Axtens Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- ...djust-the-header-paths-in-libtasn1.h.patch | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 grub-core/lib/libtasn1-patches/0004-libtasn1-adjust-the-header-paths-in-libtasn1.h.patch diff --git a/grub-core/lib/libtasn1-patches/0004-libtasn1-adjust-the-header-paths-in-libtasn1.h.patch b/grub-core/lib/libtasn1-patches/0004-libtasn1-adjust-the-header-paths-in-libtasn1.h.patch new file mode 100644 index 000000000..a5d5be417 --- /dev/null +++ b/grub-core/lib/libtasn1-patches/0004-libtasn1-adjust-the-header-paths-in-libtasn1.h.patch @@ -0,0 +1,39 @@ +From 24036474fab426917507fcadc85a83fa0b1cef3b Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Tue, 25 Jun 2024 16:30:40 +0800 +Subject: [PATCH 04/13] libtasn1: adjust the header paths in libtasn1.h + +Since libtasn1.h is the header to be included by users, including the +standard POSIX headers in libtasn1.h would force the user to add the +CFLAGS/CPPFLAGS for the POSIX headers. + +This commit adjusts the header paths to use the grub headers instead of +the standard POSIX headers, so that users only need to include +libtasn1.h to use libtasn1 functions. + +Signed-off-by: Daniel Axtens +Signed-off-by: Gary Lin +Reviewed-by: Daniel Kiper +--- + grub-core/lib/libtasn1-grub/libtasn1.h | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/grub-core/lib/libtasn1-grub/libtasn1.h b/grub-core/lib/libtasn1-grub/libtasn1.h +index 058ab27b0..7d64b6ab7 100644 +--- a/grub-core/lib/libtasn1-grub/libtasn1.h ++++ b/grub-core/lib/libtasn1-grub/libtasn1.h +@@ -54,9 +54,8 @@ + # define __LIBTASN1_PURE__ + # endif + +-# include +-# include +-# include /* for FILE* */ ++# include ++# include + + # ifdef __cplusplus + extern "C" +-- +2.43.0 + From 4160ca9839f5d4c86df75ed2ee24ba0bf43bc9f4 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:34:35 +0800 Subject: [PATCH 216/402] libtasn1: Use grub_divmod64() for division Replace a 64-bit division with a call to grub_divmod64(), preventing creation of __udivdi3() calls on 32-bit platforms. Signed-off-by: Daniel Axtens Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- ...tasn1-Use-grub_divmod64-for-division.patch | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 grub-core/lib/libtasn1-patches/0005-libtasn1-Use-grub_divmod64-for-division.patch diff --git a/grub-core/lib/libtasn1-patches/0005-libtasn1-Use-grub_divmod64-for-division.patch b/grub-core/lib/libtasn1-patches/0005-libtasn1-Use-grub_divmod64-for-division.patch new file mode 100644 index 000000000..201df10f7 --- /dev/null +++ b/grub-core/lib/libtasn1-patches/0005-libtasn1-Use-grub_divmod64-for-division.patch @@ -0,0 +1,31 @@ +From 5ee39f0bb8b0b713f766c0fc83439d83ac2c0bf2 Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Tue, 25 Jun 2024 16:32:50 +0800 +Subject: [PATCH 05/13] libtasn1: Use grub_divmod64() for division + +Replace a 64-bit division with a call to grub_divmod64(), preventing +creation of __udivdi3() calls on 32-bit platforms. + +Signed-off-by: Daniel Axtens +Signed-off-by: Gary Lin +Reviewed-by: Daniel Kiper +--- + grub-core/lib/libtasn1-grub/lib/parser_aux.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/grub-core/lib/libtasn1-grub/lib/parser_aux.c b/grub-core/lib/libtasn1-grub/lib/parser_aux.c +index c05bd2339..e4e4c0556 100644 +--- a/grub-core/lib/libtasn1-grub/lib/parser_aux.c ++++ b/grub-core/lib/libtasn1-grub/lib/parser_aux.c +@@ -632,7 +632,7 @@ _asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE]) + count = 0; + do + { +- d = val / 10; ++ d = grub_divmod64(val, 10, NULL); + r = val - d * 10; + temp[start + count] = '0' + (char) r; + count++; +-- +2.43.0 + From 0ad1d4ba861d28401e9dfb53624024aa747c171d Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:34:36 +0800 Subject: [PATCH 217/402] libtasn1: Fix the potential buffer overrun In _asn1_tag_der(), the first while loop for the long form may end up with a "k" value with "ASN1_MAX_TAG_SIZE" and cause the buffer overrun in the second while loop. This commit tweaks the conditional check to avoid producing a too large "k". This is a quick fix and may differ from the official upstream fix. libtasn1 issue: https://gitlab.com/gnutls/libtasn1/-/issues/49 Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- ...sn1-fix-the-potential-buffer-overrun.patch | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 grub-core/lib/libtasn1-patches/0006-libtasn1-fix-the-potential-buffer-overrun.patch diff --git a/grub-core/lib/libtasn1-patches/0006-libtasn1-fix-the-potential-buffer-overrun.patch b/grub-core/lib/libtasn1-patches/0006-libtasn1-fix-the-potential-buffer-overrun.patch new file mode 100644 index 000000000..f9f45ae72 --- /dev/null +++ b/grub-core/lib/libtasn1-patches/0006-libtasn1-fix-the-potential-buffer-overrun.patch @@ -0,0 +1,36 @@ +From f629f58b01b3e88052be5e50f51a667181203e05 Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Mon, 8 Apr 2024 14:57:21 +0800 +Subject: [PATCH 06/13] libtasn1: fix the potential buffer overrun + +In _asn1_tag_der(), the first while loop for the long form may end up +with a 'k' value with 'ASN1_MAX_TAG_SIZE' and cause the buffer overrun +in the second while loop. This commit tweaks the conditional check to +avoid producing a too large 'k'. + +This is a quick fix and may differ from the official upstream fix. + +libtasn1 issue: https://gitlab.com/gnutls/libtasn1/-/issues/49 + +Signed-off-by: Gary Lin +Reviewed-by: Daniel Kiper +--- + grub-core/lib/libtasn1-grub/lib/coding.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/grub-core/lib/libtasn1-grub/lib/coding.c b/grub-core/lib/libtasn1-grub/lib/coding.c +index 5d03bca9d..0458829a5 100644 +--- a/grub-core/lib/libtasn1-grub/lib/coding.c ++++ b/grub-core/lib/libtasn1-grub/lib/coding.c +@@ -143,7 +143,7 @@ _asn1_tag_der (unsigned char class, unsigned int tag_value, + temp[k++] = tag_value & 0x7F; + tag_value >>= 7; + +- if (k > ASN1_MAX_TAG_SIZE - 1) ++ if (k >= ASN1_MAX_TAG_SIZE - 1) + break; /* will not encode larger tags */ + } + *ans_len = k + 1; +-- +2.43.0 + From 54e0e19a29d06120393a95bec9f86a03632c1797 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:34:37 +0800 Subject: [PATCH 218/402] asn1_test: Include asn1_test.h only This commit removes all the headers and only uses asn1_test.h. To avoid including int.h from grub-core/lib/libtasn1-grub/lib, CONST_DOWN is defined in reproducers.c. Signed-off-by: Daniel Axtens Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- ...7-asn1_test-include-asn1_test.h-only.patch | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 grub-core/lib/libtasn1-patches/0007-asn1_test-include-asn1_test.h-only.patch diff --git a/grub-core/lib/libtasn1-patches/0007-asn1_test-include-asn1_test.h-only.patch b/grub-core/lib/libtasn1-patches/0007-asn1_test-include-asn1_test.h-only.patch new file mode 100644 index 000000000..29b931368 --- /dev/null +++ b/grub-core/lib/libtasn1-patches/0007-asn1_test-include-asn1_test.h-only.patch @@ -0,0 +1,164 @@ +From 9cc63a71d6772947127d4d77e135d5fda68ccb8d Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Fri, 16 Aug 2024 14:10:21 +0800 +Subject: [PATCH 07/13] asn1_test: include asn1_test.h only + +This commit removes all the headers and only uses asn1_test.h. +To avoid including int.h from grub-core/lib/libtasn1-grub/lib/, +CONST_DOWN is defined in reproducers.c. + +Signed-off-by: Daniel Axtens +Signed-off-by: Gary Lin +Reviewed-by: Daniel Kiper +--- + grub-core/tests/asn1/tests/CVE-2018-1000654.c | 4 +--- + grub-core/tests/asn1/tests/Test_overflow.c | 7 +------ + grub-core/tests/asn1/tests/Test_simple.c | 6 +----- + grub-core/tests/asn1/tests/Test_strings.c | 6 +----- + grub-core/tests/asn1/tests/object-id-decoding.c | 6 +----- + grub-core/tests/asn1/tests/object-id-encoding.c | 6 +----- + grub-core/tests/asn1/tests/octet-string.c | 6 +----- + grub-core/tests/asn1/tests/reproducers.c | 8 ++------ + 8 files changed, 9 insertions(+), 40 deletions(-) + +diff --git a/grub-core/tests/asn1/tests/CVE-2018-1000654.c b/grub-core/tests/asn1/tests/CVE-2018-1000654.c +index 0c22b7012..98c2a8b8c 100644 +--- a/grub-core/tests/asn1/tests/CVE-2018-1000654.c ++++ b/grub-core/tests/asn1/tests/CVE-2018-1000654.c +@@ -22,9 +22,7 @@ + /* Description: reproducer for CVE-2018-1000654 */ + /****************************************************************/ + +-#include +-#include +-#include ++#include "asn1_test.h" + #include "CVE-2018-1000654-1_asn1_tab.h" + #include "CVE-2018-1000654-2_asn1_tab.h" + +diff --git a/grub-core/tests/asn1/tests/Test_overflow.c b/grub-core/tests/asn1/tests/Test_overflow.c +index c61dea4bb..73e9d8c68 100644 +--- a/grub-core/tests/asn1/tests/Test_overflow.c ++++ b/grub-core/tests/asn1/tests/Test_overflow.c +@@ -20,12 +20,7 @@ + + /* Written by Simon Josefsson */ + +-#include +-#include +-#include +-#include +- +-#include "libtasn1.h" ++#include "asn1_test.h" + + int + main (int argc, char **argv) +diff --git a/grub-core/tests/asn1/tests/Test_simple.c b/grub-core/tests/asn1/tests/Test_simple.c +index 6cd07e069..3aa8ce21b 100644 +--- a/grub-core/tests/asn1/tests/Test_simple.c ++++ b/grub-core/tests/asn1/tests/Test_simple.c +@@ -20,11 +20,7 @@ + * + */ + +-#include +-#include +-#include +- +-#include "libtasn1.h" ++#include "asn1_test.h" + + struct tv + { +diff --git a/grub-core/tests/asn1/tests/Test_strings.c b/grub-core/tests/asn1/tests/Test_strings.c +index 27f7215e1..c49229af9 100644 +--- a/grub-core/tests/asn1/tests/Test_strings.c ++++ b/grub-core/tests/asn1/tests/Test_strings.c +@@ -20,11 +20,7 @@ + * + */ + +-#include +-#include +-#include +- +-#include "libtasn1.h" ++#include "asn1_test.h" + + struct tv + { +diff --git a/grub-core/tests/asn1/tests/object-id-decoding.c b/grub-core/tests/asn1/tests/object-id-decoding.c +index 06a6c52a2..0a77db752 100644 +--- a/grub-core/tests/asn1/tests/object-id-decoding.c ++++ b/grub-core/tests/asn1/tests/object-id-decoding.c +@@ -18,11 +18,7 @@ + * + */ + +-#include +-#include +-#include +- +-#include "libtasn1.h" ++#include "asn1_test.h" + + struct tv + { +diff --git a/grub-core/tests/asn1/tests/object-id-encoding.c b/grub-core/tests/asn1/tests/object-id-encoding.c +index 1a3396986..e32835830 100644 +--- a/grub-core/tests/asn1/tests/object-id-encoding.c ++++ b/grub-core/tests/asn1/tests/object-id-encoding.c +@@ -18,11 +18,7 @@ + * + */ + +-#include +-#include +-#include +- +-#include "libtasn1.h" ++#include "asn1_test.h" + + struct tv + { +diff --git a/grub-core/tests/asn1/tests/octet-string.c b/grub-core/tests/asn1/tests/octet-string.c +index 69eb18a62..8e803af41 100644 +--- a/grub-core/tests/asn1/tests/octet-string.c ++++ b/grub-core/tests/asn1/tests/octet-string.c +@@ -20,11 +20,7 @@ + * + */ + +-#include +-#include +-#include +- +-#include "libtasn1.h" ++#include "asn1_test.h" + + struct tv + { +diff --git a/grub-core/tests/asn1/tests/reproducers.c b/grub-core/tests/asn1/tests/reproducers.c +index a09d8b021..ce24e0991 100644 +--- a/grub-core/tests/asn1/tests/reproducers.c ++++ b/grub-core/tests/asn1/tests/reproducers.c +@@ -22,13 +22,9 @@ + /* Description: run reproducers for several fixed issues */ + /****************************************************************/ + +-#include +-#include +-#include ++#include "asn1_test.h" + +-#include +- +-#include ++#define CONST_DOWN (1U<<29) + + /* produces endless loop (fixed by d4b624b2): + * The following translates into a single node with all pointers +-- +2.43.0 + From d60a04baef5dae4c32817468355d97bc6c3f6f1c Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:34:38 +0800 Subject: [PATCH 219/402] asn1_test: Rename the main functions to the test names This commit changes the main functions in the testcases to the test names so that the real "main" test function can invokes them. Signed-off-by: Daniel Axtens Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- ...-the-main-functions-to-the-test-name.patch | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 grub-core/lib/libtasn1-patches/0008-asn1_test-rename-the-main-functions-to-the-test-name.patch diff --git a/grub-core/lib/libtasn1-patches/0008-asn1_test-rename-the-main-functions-to-the-test-name.patch b/grub-core/lib/libtasn1-patches/0008-asn1_test-rename-the-main-functions-to-the-test-name.patch new file mode 100644 index 000000000..bd34b47f8 --- /dev/null +++ b/grub-core/lib/libtasn1-patches/0008-asn1_test-rename-the-main-functions-to-the-test-name.patch @@ -0,0 +1,129 @@ +From c0f39b9eae745a36c44fc0dfa106675405ea154f Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Fri, 16 Aug 2024 14:18:44 +0800 +Subject: [PATCH 08/13] asn1_test: rename the main functions to the test names + +This commit changes the main functions in the testcases to the test +names so that the real 'main' test function can invokes them. + +Signed-off-by: Daniel Axtens +Signed-off-by: Gary Lin +Reviewed-by: Daniel Kiper +--- + grub-core/tests/asn1/tests/CVE-2018-1000654.c | 2 +- + grub-core/tests/asn1/tests/Test_overflow.c | 2 +- + grub-core/tests/asn1/tests/Test_simple.c | 2 +- + grub-core/tests/asn1/tests/Test_strings.c | 2 +- + grub-core/tests/asn1/tests/object-id-decoding.c | 2 +- + grub-core/tests/asn1/tests/object-id-encoding.c | 2 +- + grub-core/tests/asn1/tests/octet-string.c | 2 +- + grub-core/tests/asn1/tests/reproducers.c | 2 +- + 8 files changed, 8 insertions(+), 8 deletions(-) + +diff --git a/grub-core/tests/asn1/tests/CVE-2018-1000654.c b/grub-core/tests/asn1/tests/CVE-2018-1000654.c +index 98c2a8b8c..a935ab541 100644 +--- a/grub-core/tests/asn1/tests/CVE-2018-1000654.c ++++ b/grub-core/tests/asn1/tests/CVE-2018-1000654.c +@@ -27,7 +27,7 @@ + #include "CVE-2018-1000654-2_asn1_tab.h" + + int +-main (int argc, char *argv[]) ++test_CVE_2018_1000654 (void) + { + int result, verbose = 0; + asn1_node definitions = NULL; +diff --git a/grub-core/tests/asn1/tests/Test_overflow.c b/grub-core/tests/asn1/tests/Test_overflow.c +index 73e9d8c68..bc28d0826 100644 +--- a/grub-core/tests/asn1/tests/Test_overflow.c ++++ b/grub-core/tests/asn1/tests/Test_overflow.c +@@ -23,7 +23,7 @@ + #include "asn1_test.h" + + int +-main (int argc, char **argv) ++test_overflow (void) + { + /* Test that values larger than long are rejected. This has worked + fine with all versions of libtasn1. */ +diff --git a/grub-core/tests/asn1/tests/Test_simple.c b/grub-core/tests/asn1/tests/Test_simple.c +index 3aa8ce21b..12993bfba 100644 +--- a/grub-core/tests/asn1/tests/Test_simple.c ++++ b/grub-core/tests/asn1/tests/Test_simple.c +@@ -73,7 +73,7 @@ static const struct tv tv[] = { + }; + + int +-main (int argc, char *argv[]) ++test_simple (void) + { + int result; + unsigned char der[100]; +diff --git a/grub-core/tests/asn1/tests/Test_strings.c b/grub-core/tests/asn1/tests/Test_strings.c +index c49229af9..2538f2558 100644 +--- a/grub-core/tests/asn1/tests/Test_strings.c ++++ b/grub-core/tests/asn1/tests/Test_strings.c +@@ -70,7 +70,7 @@ static const struct tv ber[] = { + }; + + int +-main (int argc, char *argv[]) ++test_strings (void) + { + int ret; + unsigned char tl[ASN1_MAX_TL_SIZE]; +diff --git a/grub-core/tests/asn1/tests/object-id-decoding.c b/grub-core/tests/asn1/tests/object-id-decoding.c +index 0a77db752..fdbb8ea21 100644 +--- a/grub-core/tests/asn1/tests/object-id-decoding.c ++++ b/grub-core/tests/asn1/tests/object-id-decoding.c +@@ -70,7 +70,7 @@ static const struct tv tv[] = { + }; + + int +-main (int argc, char *argv[]) ++test_object_id_decoding (void) + { + char str[128]; + int ret, ret_len; +diff --git a/grub-core/tests/asn1/tests/object-id-encoding.c b/grub-core/tests/asn1/tests/object-id-encoding.c +index e32835830..a497015e3 100644 +--- a/grub-core/tests/asn1/tests/object-id-encoding.c ++++ b/grub-core/tests/asn1/tests/object-id-encoding.c +@@ -80,7 +80,7 @@ static const struct tv tv[] = { + }; + + int +-main (int argc, char *argv[]) ++test_object_id_encoding (void) + { + unsigned char der[128]; + int ret, der_len, i, j; +diff --git a/grub-core/tests/asn1/tests/octet-string.c b/grub-core/tests/asn1/tests/octet-string.c +index 8e803af41..8c49c6e0c 100644 +--- a/grub-core/tests/asn1/tests/octet-string.c ++++ b/grub-core/tests/asn1/tests/octet-string.c +@@ -108,7 +108,7 @@ static const struct tv tv[] = { + }; + + int +-main (int argc, char *argv[]) ++test_octet_string (void) + { + unsigned char str[100]; + unsigned char der[100]; +diff --git a/grub-core/tests/asn1/tests/reproducers.c b/grub-core/tests/asn1/tests/reproducers.c +index ce24e0991..e843b74b9 100644 +--- a/grub-core/tests/asn1/tests/reproducers.c ++++ b/grub-core/tests/asn1/tests/reproducers.c +@@ -49,7 +49,7 @@ const asn1_static_node tab[] = { + }; + + int +-main (int argc, char *argv[]) ++test_reproducers (void) + { + int result, verbose = 0; + asn1_node definitions = NULL; +-- +2.43.0 + From b7568e33588c4a12e8f21930d692a9db8fb48807 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:34:39 +0800 Subject: [PATCH 220/402] asn1_test: Return either 0 or 1 to reflect the results Some testcases use exit() to end the test. Since all the asn1 testcases are invoked as functions, this commit replaces exit() with return to reflect the test results, so that the main test function can check the results. Signed-off-by: Daniel Axtens Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- ...-either-0-or-1-to-reflect-the-result.patch | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 grub-core/lib/libtasn1-patches/0009-asn1_test-return-either-0-or-1-to-reflect-the-result.patch diff --git a/grub-core/lib/libtasn1-patches/0009-asn1_test-return-either-0-or-1-to-reflect-the-result.patch b/grub-core/lib/libtasn1-patches/0009-asn1_test-return-either-0-or-1-to-reflect-the-result.patch new file mode 100644 index 000000000..b76942d84 --- /dev/null +++ b/grub-core/lib/libtasn1-patches/0009-asn1_test-return-either-0-or-1-to-reflect-the-result.patch @@ -0,0 +1,78 @@ +From 103ec39de5277e2683099017959415e107b885a5 Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Fri, 16 Aug 2024 15:32:39 +0800 +Subject: [PATCH 09/13] asn1_test: return either 0 or 1 to reflect the results + +Some testcases use exit() to end the test. Since all the asn1 testcases +are invoked as functions, this commit replaces exit() with return to +reflect the test results, so that the main test function can check the +results. + +Signed-off-by: Daniel Axtens +Signed-off-by: Gary Lin +Reviewed-by: Daniel Kiper +--- + grub-core/tests/asn1/tests/CVE-2018-1000654.c | 6 +++--- + grub-core/tests/asn1/tests/reproducers.c | 6 +++--- + 2 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/grub-core/tests/asn1/tests/CVE-2018-1000654.c b/grub-core/tests/asn1/tests/CVE-2018-1000654.c +index a935ab541..2085b3cd3 100644 +--- a/grub-core/tests/asn1/tests/CVE-2018-1000654.c ++++ b/grub-core/tests/asn1/tests/CVE-2018-1000654.c +@@ -45,7 +45,7 @@ test_CVE_2018_1000654 (void) + { + asn1_perror (result); + printf ("ErrorDescription = %s\n\n", errorDescription); +- exit (1); ++ return 1; + } + + asn1_delete_structure (&definitions); +@@ -59,12 +59,12 @@ test_CVE_2018_1000654 (void) + { + asn1_perror (result); + printf ("ErrorDescription = %s\n\n", errorDescription); +- exit (1); ++ return 1; + } + + asn1_delete_structure (&definitions); + + if (verbose) + printf ("Success\n"); +- exit (0); ++ return 0; + } +diff --git a/grub-core/tests/asn1/tests/reproducers.c b/grub-core/tests/asn1/tests/reproducers.c +index e843b74b9..54a243d51 100644 +--- a/grub-core/tests/asn1/tests/reproducers.c ++++ b/grub-core/tests/asn1/tests/reproducers.c +@@ -63,7 +63,7 @@ test_reproducers (void) + { + asn1_perror (result); + printf ("ErrorDescription = %s\n\n", errorDescription); +- exit (EXIT_FAILURE); ++ return 1; + } + + asn1_delete_structure (&definitions); +@@ -74,7 +74,7 @@ test_reproducers (void) + { + asn1_perror (result); + printf ("ErrorDescription = %s\n\n", errorDescription); +- exit (EXIT_FAILURE); ++ return 1; + } + + asn1_delete_structure (&definitions); +@@ -82,5 +82,5 @@ test_reproducers (void) + if (verbose) + printf ("Success\n"); + +- exit (EXIT_SUCCESS); ++ return 0; + } +-- +2.43.0 + From 2e93a8e4bd73eb9647331550a306f66ec01a6102 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:34:40 +0800 Subject: [PATCH 221/402] asn1_test: Remove "verbose" and the unnecessary printf() This commit removes the "verbose" variables and the unnecessary printf() to simplify the output. Signed-off-by: Daniel Axtens Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- ...e-verbose-and-the-unnecessary-printf.patch | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 grub-core/lib/libtasn1-patches/0010-asn1_test-remove-verbose-and-the-unnecessary-printf.patch diff --git a/grub-core/lib/libtasn1-patches/0010-asn1_test-remove-verbose-and-the-unnecessary-printf.patch b/grub-core/lib/libtasn1-patches/0010-asn1_test-remove-verbose-and-the-unnecessary-printf.patch new file mode 100644 index 000000000..2bd8ffb48 --- /dev/null +++ b/grub-core/lib/libtasn1-patches/0010-asn1_test-remove-verbose-and-the-unnecessary-printf.patch @@ -0,0 +1,173 @@ +From 759bc707a924f89b0d32ba7be2f54e100f1d57b7 Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Fri, 16 Aug 2024 14:26:38 +0800 +Subject: [PATCH 10/13] asn1_test: remove 'verbose' and the unnecessary + printf() + +This commit removes the 'verbose' variables and the unnecessary printf() +to simplify the output. + +Signed-off-by: Daniel Axtens +Signed-off-by: Gary Lin +Reviewed-by: Daniel Kiper +--- + grub-core/tests/asn1/tests/CVE-2018-1000654.c | 11 +---- + grub-core/tests/asn1/tests/Test_overflow.c | 40 +++---------------- + grub-core/tests/asn1/tests/reproducers.c | 8 +--- + 3 files changed, 7 insertions(+), 52 deletions(-) + +diff --git a/grub-core/tests/asn1/tests/CVE-2018-1000654.c b/grub-core/tests/asn1/tests/CVE-2018-1000654.c +index 2085b3cd3..81448cde8 100644 +--- a/grub-core/tests/asn1/tests/CVE-2018-1000654.c ++++ b/grub-core/tests/asn1/tests/CVE-2018-1000654.c +@@ -29,15 +29,10 @@ + int + test_CVE_2018_1000654 (void) + { +- int result, verbose = 0; ++ int result; + asn1_node definitions = NULL; + char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + +- if (argc > 1) +- verbose = 1; +- +- printf ("Test 1\n"); +- + result = + asn1_array2tree (CVE_2018_1000654_1_asn1_tab, &definitions, + errorDescription); +@@ -50,8 +45,6 @@ test_CVE_2018_1000654 (void) + + asn1_delete_structure (&definitions); + +- printf ("Test 2\n"); +- + result = + asn1_array2tree (CVE_2018_1000654_2_asn1_tab, &definitions, + errorDescription); +@@ -64,7 +57,5 @@ test_CVE_2018_1000654 (void) + + asn1_delete_structure (&definitions); + +- if (verbose) +- printf ("Success\n"); + return 0; + } +diff --git a/grub-core/tests/asn1/tests/Test_overflow.c b/grub-core/tests/asn1/tests/Test_overflow.c +index bc28d0826..9f9578a1f 100644 +--- a/grub-core/tests/asn1/tests/Test_overflow.c ++++ b/grub-core/tests/asn1/tests/Test_overflow.c +@@ -27,11 +27,6 @@ test_overflow (void) + { + /* Test that values larger than long are rejected. This has worked + fine with all versions of libtasn1. */ +- int verbose = 0; +- +- if (argc > 1) +- verbose = 1; +- + { + unsigned char der[] = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"; + long l; +@@ -39,12 +34,7 @@ test_overflow (void) + + l = asn1_get_length_der (der, sizeof der, &len); + +- if (l == -2L) +- { +- if (verbose) +- puts ("OK: asn1_get_length_der bignum"); +- } +- else ++ if (l != -2L) + { + printf ("ERROR: asn1_get_length_der bignum (l %ld len %d)\n", l, len); + return 1; +@@ -65,12 +55,7 @@ test_overflow (void) + + l = asn1_get_length_der (der, der_len, &len); + +- if (l == -2L) +- { +- if (verbose) +- puts ("OK: asn1_get_length_der intnum"); +- } +- else ++ if (l != -2L) + { + printf ("ERROR: asn1_get_length_der intnum (l %ld len %d)\n", l, + len); +@@ -92,12 +77,7 @@ test_overflow (void) + der_len = sizeof (der); + l = asn1_get_length_der (der, der_len, &len); + +- if (l == -4L) +- { +- if (verbose) +- puts ("OK: asn1_get_length_der overflow-small"); +- } +- else ++ if (l != -4L) + { + printf ("ERROR: asn1_get_length_der overflow-small (l %ld len %d)\n", + l, len); +@@ -119,12 +99,7 @@ test_overflow (void) + der_len = sizeof (der); + l = asn1_get_length_der (der, der_len, &len); + +- if (l == -4L) +- { +- if (verbose) +- puts ("OK: asn1_get_length_der overflow-large1"); +- } +- else ++ if (l != -4L) + { + printf ("ERROR: asn1_get_length_der overflow-large1 (l %ld len %d)\n", + l, len); +@@ -146,12 +121,7 @@ test_overflow (void) + der_len = sizeof (der); + l = asn1_get_length_der (der, der_len, &len); + +- if (l == -2L) +- { +- if (verbose) +- puts ("OK: asn1_get_length_der overflow-large2"); +- } +- else ++ if (l != -2L) + { + printf ("ERROR: asn1_get_length_der overflow-large2 (l %ld len %d)\n", + l, len); +diff --git a/grub-core/tests/asn1/tests/reproducers.c b/grub-core/tests/asn1/tests/reproducers.c +index 54a243d51..fa3cea762 100644 +--- a/grub-core/tests/asn1/tests/reproducers.c ++++ b/grub-core/tests/asn1/tests/reproducers.c +@@ -51,13 +51,10 @@ const asn1_static_node tab[] = { + int + test_reproducers (void) + { +- int result, verbose = 0; ++ int result; + asn1_node definitions = NULL; + char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + +- if (argc > 1) +- verbose = 1; +- + result = asn1_array2tree (endless_asn1_tab, &definitions, errorDescription); + if (result != ASN1_SUCCESS) + { +@@ -79,8 +76,5 @@ test_reproducers (void) + + asn1_delete_structure (&definitions); + +- if (verbose) +- printf ("Success\n"); +- + return 0; + } +-- +2.43.0 + From 0d0913fc61437710028ae06e53ee159ced06c802 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:34:41 +0800 Subject: [PATCH 222/402] asn1_test: Print the error messages with grub_printf() This commit replaces printf() and fprintf() with grub_printf() to print the error messages for the testcases. Besides, asn1_strerror() is used to convert the result code to strings instead of asn1_perror(). Signed-off-by: Daniel Axtens Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- ...-the-error-messages-with-grub_printf.patch | 485 ++++++++++++++++++ 1 file changed, 485 insertions(+) create mode 100644 grub-core/lib/libtasn1-patches/0011-asn1_test-print-the-error-messages-with-grub_printf.patch diff --git a/grub-core/lib/libtasn1-patches/0011-asn1_test-print-the-error-messages-with-grub_printf.patch b/grub-core/lib/libtasn1-patches/0011-asn1_test-print-the-error-messages-with-grub_printf.patch new file mode 100644 index 000000000..aadd362cd --- /dev/null +++ b/grub-core/lib/libtasn1-patches/0011-asn1_test-print-the-error-messages-with-grub_printf.patch @@ -0,0 +1,485 @@ +From af5f0a651de09e44e78177b988857acd3208c4b6 Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Fri, 16 Aug 2024 15:00:42 +0800 +Subject: [PATCH 11/13] asn1_test: print the error messages with grub_printf() + +This commit replaces printf() and fprintf() with grub_printf() to print +the error messages for the testcases. Besides, asn1_strerror() is used +to convert the result code to strings instead of asn1_perror(). + +Signed-off-by: Daniel Axtens +Signed-off-by: Gary Lin +Reviewed-by: Daniel Kiper +--- + grub-core/tests/asn1/tests/CVE-2018-1000654.c | 8 +-- + grub-core/tests/asn1/tests/Test_overflow.c | 14 ++--- + grub-core/tests/asn1/tests/Test_simple.c | 21 ++++---- + grub-core/tests/asn1/tests/Test_strings.c | 21 +++----- + .../tests/asn1/tests/object-id-decoding.c | 16 +++--- + .../tests/asn1/tests/object-id-encoding.c | 26 ++++----- + grub-core/tests/asn1/tests/octet-string.c | 54 ++++++++----------- + grub-core/tests/asn1/tests/reproducers.c | 6 +-- + 8 files changed, 67 insertions(+), 99 deletions(-) + +diff --git a/grub-core/tests/asn1/tests/CVE-2018-1000654.c b/grub-core/tests/asn1/tests/CVE-2018-1000654.c +index 81448cde8..b78667900 100644 +--- a/grub-core/tests/asn1/tests/CVE-2018-1000654.c ++++ b/grub-core/tests/asn1/tests/CVE-2018-1000654.c +@@ -38,8 +38,8 @@ test_CVE_2018_1000654 (void) + errorDescription); + if (result != ASN1_RECURSION) + { +- asn1_perror (result); +- printf ("ErrorDescription = %s\n\n", errorDescription); ++ grub_printf ("Error: %s\nErrorDescription = %s\n\n", ++ asn1_strerror (result), errorDescription); + return 1; + } + +@@ -50,8 +50,8 @@ test_CVE_2018_1000654 (void) + errorDescription); + if (result != ASN1_RECURSION) + { +- asn1_perror (result); +- printf ("ErrorDescription = %s\n\n", errorDescription); ++ grub_printf ("Error: %s\nErrorDescription = %s\n\n", ++ asn1_strerror (result), errorDescription); + return 1; + } + +diff --git a/grub-core/tests/asn1/tests/Test_overflow.c b/grub-core/tests/asn1/tests/Test_overflow.c +index 9f9578a1f..ffac8507a 100644 +--- a/grub-core/tests/asn1/tests/Test_overflow.c ++++ b/grub-core/tests/asn1/tests/Test_overflow.c +@@ -36,7 +36,7 @@ test_overflow (void) + + if (l != -2L) + { +- printf ("ERROR: asn1_get_length_der bignum (l %ld len %d)\n", l, len); ++ grub_printf ("ERROR: asn1_get_length_der bignum (l %ld len %d)\n", l, len); + return 1; + } + } +@@ -57,8 +57,7 @@ test_overflow (void) + + if (l != -2L) + { +- printf ("ERROR: asn1_get_length_der intnum (l %ld len %d)\n", l, +- len); ++ grub_printf ("ERROR: asn1_get_length_der intnum (l %ld len %d)\n", l, len); + return 1; + } + } +@@ -79,8 +78,7 @@ test_overflow (void) + + if (l != -4L) + { +- printf ("ERROR: asn1_get_length_der overflow-small (l %ld len %d)\n", +- l, len); ++ grub_printf ("ERROR: asn1_get_length_der overflow-small (l %ld len %d)\n", l, len); + return 1; + } + } +@@ -101,8 +99,7 @@ test_overflow (void) + + if (l != -4L) + { +- printf ("ERROR: asn1_get_length_der overflow-large1 (l %ld len %d)\n", +- l, len); ++ grub_printf ("ERROR: asn1_get_length_der overflow-large1 (l %ld len %d)\n", l, len); + return 1; + } + } +@@ -123,8 +120,7 @@ test_overflow (void) + + if (l != -2L) + { +- printf ("ERROR: asn1_get_length_der overflow-large2 (l %ld len %d)\n", +- l, len); ++ grub_printf ("ERROR: asn1_get_length_der overflow-large2 (l %ld len %d)\n", l, len); + return 1; + } + } +diff --git a/grub-core/tests/asn1/tests/Test_simple.c b/grub-core/tests/asn1/tests/Test_simple.c +index 12993bfba..dc70db191 100644 +--- a/grub-core/tests/asn1/tests/Test_simple.c ++++ b/grub-core/tests/asn1/tests/Test_simple.c +@@ -94,7 +94,7 @@ test_simple (void) + result = asn1_encode_simple_der (etype, my_str, my_str_len, tl, &tl_len); + if (result != ASN1_VALUE_NOT_VALID) + { +- fprintf (stderr, "asn1_encode_simple_der out of range etype\n"); ++ grub_printf ("asn1_encode_simple_der out of range etype\n"); + return 1; + } + } +@@ -105,7 +105,7 @@ test_simple (void) + result = asn1_get_bit_der (der, 0, &ret_len, str, str_size, &bit_len); + if (result != ASN1_GENERIC_ERROR) + { +- fprintf (stderr, "asn1_get_bit_der zero\n"); ++ grub_printf ("asn1_get_bit_der zero\n"); + return 1; + } + +@@ -129,7 +129,7 @@ test_simple (void) + + if (der_len != tv[i].derlen || memcmp (der, tv[i].der, der_len) != 0) + { +- fprintf (stderr, "asn1_bit_der iter %lu\n", (unsigned long) i); ++ grub_printf ("asn1_bit_der iter %lu\n", (unsigned long) i); + return 1; + } + +@@ -140,8 +140,7 @@ test_simple (void) + if (result != ASN1_SUCCESS || ret_len != tv[i].derlen + || bit_len != tv[i].bitlen) + { +- fprintf (stderr, "asn1_get_bit_der iter %lu, err: %d\n", +- (unsigned long) i, result); ++ grub_printf ("asn1_get_bit_der iter %lu, err: %d\n", (unsigned long) i, result); + return 1; + } + } +@@ -163,7 +162,7 @@ test_simple (void) + if (result != ASN1_SUCCESS || ret_len != 5 + || bit_len != 18 || memcmp (str, "\x6e\x5d\xc0", 3) != 0) + { +- fprintf (stderr, "asn1_get_bit_der example\n"); ++ grub_printf ("asn1_get_bit_der example\n"); + return 1; + } + +@@ -171,7 +170,7 @@ test_simple (void) + asn1_bit_der (str, bit_len, der, &der_len); + if (der_len != 5 || memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0) + { +- fprintf (stderr, "asn1_bit_der example roundtrip\n"); ++ grub_printf ("asn1_bit_der example roundtrip\n"); + return 1; + } + +@@ -184,7 +183,7 @@ test_simple (void) + if (result != ASN1_SUCCESS || ret_len != 5 + || bit_len != 18 || memcmp (str, "\x6e\x5d\xe0", 3) != 0) + { +- fprintf (stderr, "asn1_get_bit_der example padded\n"); ++ grub_printf ("asn1_get_bit_der example padded\n"); + return 1; + } + +@@ -192,7 +191,7 @@ test_simple (void) + asn1_bit_der (str, bit_len, der, &der_len); + if (der_len != 5 || memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0) + { +- fprintf (stderr, "asn1_bit_der example roundtrip\n"); ++ grub_printf ("asn1_bit_der example roundtrip\n"); + return 1; + } + +@@ -206,7 +205,7 @@ test_simple (void) + if (result != ASN1_SUCCESS || ret_len != 6 + || bit_len != 18 || memcmp (str, "\x6e\x5d\xc0", 3) != 0) + { +- fprintf (stderr, "asn1_get_bit_der example long form\n"); ++ grub_printf ("asn1_get_bit_der example long form\n"); + return 1; + } + +@@ -214,7 +213,7 @@ test_simple (void) + asn1_bit_der (str, bit_len, der, &der_len); + if (der_len != 5 || memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0) + { +- fprintf (stderr, "asn1_bit_der example roundtrip\n"); ++ grub_printf ("asn1_bit_der example roundtrip\n"); + return 1; + } + +diff --git a/grub-core/tests/asn1/tests/Test_strings.c b/grub-core/tests/asn1/tests/Test_strings.c +index 2538f2558..65c30937f 100644 +--- a/grub-core/tests/asn1/tests/Test_strings.c ++++ b/grub-core/tests/asn1/tests/Test_strings.c +@@ -89,17 +89,14 @@ test_strings (void) + tl, &tl_len); + if (ret != ASN1_SUCCESS) + { +- fprintf (stderr, "Encoding error in %u: %s\n", i, +- asn1_strerror (ret)); ++ grub_printf ("Encoding error in %u: %s\n", i, asn1_strerror (ret)); + return 1; + } + der_len = tl_len + tv[i].str_len; + + if (der_len != tv[i].der_len || memcmp (tl, tv[i].der, tl_len) != 0) + { +- fprintf (stderr, +- "DER encoding differs in %u! (size: %u, expected: %u)\n", +- i, der_len, tv[i].der_len); ++ grub_printf ("DER encoding differs in %u! (size: %u, expected: %u)\n", i, der_len, tv[i].der_len); + return 1; + } + +@@ -109,16 +106,13 @@ test_strings (void) + &str_len); + if (ret != ASN1_SUCCESS) + { +- fprintf (stderr, "Decoding error in %u: %s\n", i, +- asn1_strerror (ret)); ++ grub_printf ("Decoding error in %u: %s\n", i, asn1_strerror (ret)); + return 1; + } + + if (str_len != tv[i].str_len || memcmp (str, tv[i].str, str_len) != 0) + { +- fprintf (stderr, +- "DER decoded data differ in %u! (size: %u, expected: %u)\n", +- i, der_len, tv[i].str_len); ++ grub_printf ("DER decoded data differ in %u! (size: %u, expected: %u)\n", i, der_len, tv[i].str_len); + return 1; + } + } +@@ -132,16 +126,13 @@ test_strings (void) + &str_len, NULL); + if (ret != ASN1_SUCCESS) + { +- fprintf (stderr, "BER decoding error in %u: %s\n", i, +- asn1_strerror (ret)); ++ grub_printf ("BER decoding error in %u: %s\n", i, asn1_strerror (ret)); + return 1; + } + + if (str_len != ber[i].str_len || memcmp (b, ber[i].str, str_len) != 0) + { +- fprintf (stderr, +- "BER decoded data differ in %u! (size: %u, expected: %u)\n", +- i, str_len, ber[i].str_len); ++ grub_printf ("BER decoded data differ in %u! (size: %u, expected: %u)\n", i, str_len, ber[i].str_len); + return 1; + } + free (b); +diff --git a/grub-core/tests/asn1/tests/object-id-decoding.c b/grub-core/tests/asn1/tests/object-id-decoding.c +index fdbb8ea21..c087b46e3 100644 +--- a/grub-core/tests/asn1/tests/object-id-decoding.c ++++ b/grub-core/tests/asn1/tests/object-id-decoding.c +@@ -85,10 +85,8 @@ test_object_id_decoding (void) + sizeof (str)); + if (ret != tv[i].expected_error) + { +- fprintf (stderr, +- "%d: asn1_get_object_id_der iter %lu: got '%s' expected %d\n", +- __LINE__, (unsigned long) i, asn1_strerror (ret), +- tv[i].expected_error); ++ grub_printf ("%d: asn1_get_object_id_der iter %lu: got '%s' expected %d\n", ++ __LINE__, (unsigned long) i, asn1_strerror (ret), tv[i].expected_error); + return 1; + } + +@@ -97,17 +95,15 @@ test_object_id_decoding (void) + + if (ret_len != tv[i].der_len - 1) + { +- fprintf (stderr, +- "%d: iter %lu: error in DER, length returned is %d, had %d\n", +- __LINE__, (unsigned long) i, ret_len, tv[i].der_len - 1); ++ grub_printf ("%d: iter %lu: error in DER, length returned is %d, had %d\n", ++ __LINE__, (unsigned long) i, ret_len, tv[i].der_len - 1); + return 1; + } + + if (strcmp (tv[i].oid, str) != 0) + { +- fprintf (stderr, +- "%d: strcmp iter %lu: got invalid OID: %s, expected: %s\n", +- __LINE__, (unsigned long) i, str, tv[i].oid); ++ grub_printf ("%d: strcmp iter %lu: got invalid OID: %s, expected: %s\n", ++ __LINE__, (unsigned long) i, str, tv[i].oid); + return 1; + } + +diff --git a/grub-core/tests/asn1/tests/object-id-encoding.c b/grub-core/tests/asn1/tests/object-id-encoding.c +index a497015e3..e3da092cc 100644 +--- a/grub-core/tests/asn1/tests/object-id-encoding.c ++++ b/grub-core/tests/asn1/tests/object-id-encoding.c +@@ -93,33 +93,29 @@ test_object_id_encoding (void) + { + if (ret == tv[i].expected_error) + continue; +- fprintf (stderr, +- "%d: iter %lu, encoding of OID failed: %s\n", +- __LINE__, (unsigned long) i, asn1_strerror (ret)); ++ grub_printf ("%d: iter %lu, encoding of OID failed: %s\n", ++ __LINE__, (unsigned long) i, asn1_strerror (ret)); + return 1; + } + else if (ret != tv[i].expected_error) + { +- fprintf (stderr, +- "%d: iter %lu, encoding of OID %s succeeded when expecting failure\n", +- __LINE__, (unsigned long) i, tv[i].oid); ++ grub_printf ("%d: iter %lu, encoding of OID %s succeeded when expecting failure\n", ++ __LINE__, (unsigned long) i, tv[i].oid); + return 1; + } + + if (der_len != tv[i].der_len || memcmp (der, tv[i].der, der_len) != 0) + { +- fprintf (stderr, +- "%d: iter %lu, re-encoding of OID %s resulted to different string (%d vs %d bytes)\n", +- __LINE__, (unsigned long) i, tv[i].oid, der_len, +- tv[i].der_len); +- fprintf (stderr, "\nGot:\t\t"); ++ grub_printf ("%d: iter %lu, re-encoding of OID %s resulted to different string (%d vs %d bytes)\n", ++ __LINE__, (unsigned long) i, tv[i].oid, der_len, tv[i].der_len); ++ grub_printf ("\nGot:\t\t"); + for (j = 0; j < der_len; j++) +- fprintf (stderr, "%.2x", der[j]); ++ grub_printf ("%.2x", der[j]); + +- fprintf (stderr, "\nExpected:\t"); ++ grub_printf ("\nExpected:\t"); + for (j = 0; j < tv[i].der_len; j++) +- fprintf (stderr, "%.2x", tv[i].der[j]); +- fprintf (stderr, "\n"); ++ grub_printf ("%.2x", tv[i].der[j]); ++ grub_printf ("\n"); + + return 1; + } +diff --git a/grub-core/tests/asn1/tests/octet-string.c b/grub-core/tests/asn1/tests/octet-string.c +index 8c49c6e0c..d3a35dff8 100644 +--- a/grub-core/tests/asn1/tests/octet-string.c ++++ b/grub-core/tests/asn1/tests/octet-string.c +@@ -131,9 +131,8 @@ test_octet_string (void) + sizeof (str), &str_size); + if (ret != tv[i].expected_error) + { +- fprintf (stderr, +- "%d: asn1_get_octet_der: %s: got %d expected %d\n", +- __LINE__, tv[i].name, ret, tv[i].expected_error); ++ grub_printf ("%d: asn1_get_octet_der: %s: got %d expected %d\n", ++ __LINE__, tv[i].name, ret, tv[i].expected_error); + return 1; + } + if (tv[i].expected_error) +@@ -141,27 +140,25 @@ test_octet_string (void) + + if (ret_len != tv[i].der_len - 1) + { +- fprintf (stderr, +- "%d: error in DER, length returned is %d, had %d\n", +- __LINE__, ret_len, tv[i].der_len - 1); ++ grub_printf ("%d: error in DER, length returned is %d, had %d\n", ++ __LINE__, ret_len, tv[i].der_len - 1); + return 1; + } + + if (str_size != tv[i].len + || memcmp (tv[i].string, str, tv[i].len) != 0) + { +- fprintf (stderr, +- "%d: memcmp: %s: got invalid decoding\n", +- __LINE__, tv[i].name); ++ grub_printf ("%d: memcmp: %s: got invalid decoding\n", ++ __LINE__, tv[i].name); + +- fprintf (stderr, "\nGot:\t\t"); ++ grub_printf ("\nGot:\t\t"); + for (j = 0; j < str_size; j++) +- fprintf (stderr, "%.2x", str[j]); ++ grub_printf ("%.2x", str[j]); + +- fprintf (stderr, "\nExpected:\t"); ++ grub_printf ("\nExpected:\t"); + for (j = 0; j < tv[i].len; j++) +- fprintf (stderr, "%.2x", tv[i].string[j]); +- fprintf (stderr, "\n"); ++ grub_printf ("%.2x", tv[i].string[j]); ++ grub_printf ("\n"); + return 1; + } + +@@ -172,8 +169,7 @@ test_octet_string (void) + if (der_len != tv[i].der_len - 1 + || memcmp (tv[i].der_str + 1, der, tv[i].der_len - 1) != 0) + { +- fprintf (stderr, +- "encoding: %s: got invalid encoding\n", tv[i].name); ++ grub_printf ("encoding: %s: got invalid encoding\n", tv[i].name); + return 1; + } + } +@@ -185,10 +181,9 @@ test_octet_string (void) + (unsigned int *) &der_len); + if (ret != tv[i].expected_error) + { +- fprintf (stderr, +- "%d: asn1_decode_simple_ber: %s: got %s expected %s\n", +- __LINE__, tv[i].name, asn1_strerror (ret), +- asn1_strerror (tv[i].expected_error)); ++ grub_printf ("%d: asn1_decode_simple_ber: %s: got %s expected %s\n", ++ __LINE__, tv[i].name, asn1_strerror (ret), ++ asn1_strerror (tv[i].expected_error)); + return 1; + } + if (tv[i].expected_error) +@@ -196,25 +191,22 @@ test_octet_string (void) + + if (der_len != tv[i].der_len) + { +- fprintf (stderr, +- "%d: error: %s: DER, length returned is %d, had %d\n", +- __LINE__, tv[i].name, der_len, tv[i].der_len); ++ grub_printf ("%d: error: %s: DER, length returned is %d, had %d\n", ++ __LINE__, tv[i].name, der_len, tv[i].der_len); + return 1; + } + + if (str_size != tv[i].len || memcmp (tv[i].string, tmp, tv[i].len) != 0) + { +- fprintf (stderr, +- "%d: memcmp: %s: got invalid decoding\n", +- __LINE__, tv[i].name); +- fprintf (stderr, "\nGot:\t\t"); ++ grub_printf ("%d: memcmp: %s: got invalid decoding\n", __LINE__, tv[i].name); ++ grub_printf ("\nGot:\t\t"); + for (j = 0; j < str_size; j++) +- fprintf (stderr, "%.2x", tmp[j]); ++ grub_printf ("%.2x", tmp[j]); + +- fprintf (stderr, "\nExpected:\t"); ++ grub_printf ("\nExpected:\t"); + for (j = 0; j < tv[i].len; j++) +- fprintf (stderr, "%.2x", tv[i].string[j]); +- fprintf (stderr, "\n"); ++ grub_printf ("%.2x", tv[i].string[j]); ++ grub_printf ("\n"); + return 1; + } + free (tmp); +diff --git a/grub-core/tests/asn1/tests/reproducers.c b/grub-core/tests/asn1/tests/reproducers.c +index fa3cea762..0e3c9fd65 100644 +--- a/grub-core/tests/asn1/tests/reproducers.c ++++ b/grub-core/tests/asn1/tests/reproducers.c +@@ -58,8 +58,7 @@ test_reproducers (void) + result = asn1_array2tree (endless_asn1_tab, &definitions, errorDescription); + if (result != ASN1_SUCCESS) + { +- asn1_perror (result); +- printf ("ErrorDescription = %s\n\n", errorDescription); ++ grub_printf ("Error: %s\nErrorDescription = %s\n\n", asn1_strerror (result), errorDescription); + return 1; + } + +@@ -69,8 +68,7 @@ test_reproducers (void) + result = asn1_array2tree (tab, &definitions, errorDescription); + if (result != ASN1_SUCCESS) + { +- asn1_perror (result); +- printf ("ErrorDescription = %s\n\n", errorDescription); ++ grub_printf ("Error: %s\nErrorDescription = %s\n\n", asn1_strerror (result), errorDescription); + return 1; + } + +-- +2.43.0 + From 66cf4cb1448052cc5b04757821071917c82340ad Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:34:42 +0800 Subject: [PATCH 223/402] asn1_test: Use the grub-specific functions and types This commit converts functions and types to the grub-specific ones: - LONG_MAX -> GRUB_LONG_MAX, - INT_MAX -> GRUB_INT_MAX, - UINT_MAX -> GRUB_UINT_MAX, - size_t -> grub_size_t, - memcmp() -> grub_memcmp(), - memcpy() -> grub_memcpy(), - free() -> grub_free(), - strcmp() -> grub_strcmp(). Signed-off-by: Daniel Axtens Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- ...he-grub-specific-functions-and-types.patch | 263 ++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 grub-core/lib/libtasn1-patches/0012-asn1_test-use-the-grub-specific-functions-and-types.patch diff --git a/grub-core/lib/libtasn1-patches/0012-asn1_test-use-the-grub-specific-functions-and-types.patch b/grub-core/lib/libtasn1-patches/0012-asn1_test-use-the-grub-specific-functions-and-types.patch new file mode 100644 index 000000000..ba2715751 --- /dev/null +++ b/grub-core/lib/libtasn1-patches/0012-asn1_test-use-the-grub-specific-functions-and-types.patch @@ -0,0 +1,263 @@ +From 392da24a8a4b2ab94a93e9e0f74db027a3a9374d Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Fri, 16 Aug 2024 15:46:59 +0800 +Subject: [PATCH 12/13] asn1_test: use the grub-specific functions and types + +This commit converts functions and types to the grub-specific ones: + +LONG_MAX -> GRUB_LONG_MAX +INT_MAX -> GRUB_INT_MAX +UINT_MAX -> GRUB_UINT_MAX +size_t -> grub_size_t +memcmp() -> grub_memcmp() +memcpy() -> grub_memcpy() +free() -> grub_free() +strcmp() -> grub_strcmp() + +Signed-off-by: Daniel Axtens +Signed-off-by: Gary Lin +Reviewed-by: Daniel Kiper +--- + grub-core/tests/asn1/tests/Test_overflow.c | 4 ++-- + grub-core/tests/asn1/tests/Test_simple.c | 22 +++++++++---------- + grub-core/tests/asn1/tests/Test_strings.c | 8 +++---- + .../tests/asn1/tests/object-id-decoding.c | 4 ++-- + .../tests/asn1/tests/object-id-encoding.c | 2 +- + grub-core/tests/asn1/tests/octet-string.c | 10 ++++----- + 6 files changed, 25 insertions(+), 25 deletions(-) + +diff --git a/grub-core/tests/asn1/tests/Test_overflow.c b/grub-core/tests/asn1/tests/Test_overflow.c +index ffac8507a..65843abf6 100644 +--- a/grub-core/tests/asn1/tests/Test_overflow.c ++++ b/grub-core/tests/asn1/tests/Test_overflow.c +@@ -43,9 +43,9 @@ test_overflow (void) + + /* Test that values larger than int but smaller than long are + rejected. This limitation was introduced with libtasn1 2.12. */ +- if (LONG_MAX > INT_MAX) ++ if (GRUB_LONG_MAX > GRUB_INT_MAX) + { +- unsigned long num = ((long) UINT_MAX) << 2; ++ unsigned long num = ((long) GRUB_UINT_MAX) << 2; + unsigned char der[20]; + int der_len; + long l; +diff --git a/grub-core/tests/asn1/tests/Test_simple.c b/grub-core/tests/asn1/tests/Test_simple.c +index dc70db191..19613cae8 100644 +--- a/grub-core/tests/asn1/tests/Test_simple.c ++++ b/grub-core/tests/asn1/tests/Test_simple.c +@@ -81,7 +81,7 @@ test_simple (void) + int der_len = sizeof (der); + int str_size = sizeof (str); + int ret_len, bit_len; +- size_t i; ++ grub_size_t i; + + { + unsigned int etype = 38; +@@ -127,7 +127,7 @@ test_simple (void) + } + #endif + +- if (der_len != tv[i].derlen || memcmp (der, tv[i].der, der_len) != 0) ++ if (der_len != tv[i].derlen || grub_memcmp (der, tv[i].der, der_len) != 0) + { + grub_printf ("asn1_bit_der iter %lu\n", (unsigned long) i); + return 1; +@@ -155,12 +155,12 @@ test_simple (void) + + /* 03 04 06 6e 5d c0 DER encoding */ + +- memcpy (der, "\x04\x06\x6e\x5d\xc0", 5); ++ grub_memcpy (der, "\x04\x06\x6e\x5d\xc0", 5); + der_len = 5; + + result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len); + if (result != ASN1_SUCCESS || ret_len != 5 +- || bit_len != 18 || memcmp (str, "\x6e\x5d\xc0", 3) != 0) ++ || bit_len != 18 || grub_memcmp (str, "\x6e\x5d\xc0", 3) != 0) + { + grub_printf ("asn1_get_bit_der example\n"); + return 1; +@@ -168,7 +168,7 @@ test_simple (void) + + der_len = sizeof (der); + asn1_bit_der (str, bit_len, der, &der_len); +- if (der_len != 5 || memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0) ++ if (der_len != 5 || grub_memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0) + { + grub_printf ("asn1_bit_der example roundtrip\n"); + return 1; +@@ -176,12 +176,12 @@ test_simple (void) + + /* 03 04 06 6e 5d e0 padded with "100000" */ + +- memcpy (der, "\x04\x06\x6e\x5d\xe0", 5); ++ grub_memcpy (der, "\x04\x06\x6e\x5d\xe0", 5); + der_len = 5; + + result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len); + if (result != ASN1_SUCCESS || ret_len != 5 +- || bit_len != 18 || memcmp (str, "\x6e\x5d\xe0", 3) != 0) ++ || bit_len != 18 || grub_memcmp (str, "\x6e\x5d\xe0", 3) != 0) + { + grub_printf ("asn1_get_bit_der example padded\n"); + return 1; +@@ -189,7 +189,7 @@ test_simple (void) + + der_len = sizeof (der); + asn1_bit_der (str, bit_len, der, &der_len); +- if (der_len != 5 || memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0) ++ if (der_len != 5 || grub_memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0) + { + grub_printf ("asn1_bit_der example roundtrip\n"); + return 1; +@@ -197,13 +197,13 @@ test_simple (void) + + /* 03 81 04 06 6e 5d c0 long form of length octets */ + +- memcpy (der, "\x81\x04\x06\x6e\x5d\xc0", 6); ++ grub_memcpy (der, "\x81\x04\x06\x6e\x5d\xc0", 6); + der_len = 6; + + result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len); + + if (result != ASN1_SUCCESS || ret_len != 6 +- || bit_len != 18 || memcmp (str, "\x6e\x5d\xc0", 3) != 0) ++ || bit_len != 18 || grub_memcmp (str, "\x6e\x5d\xc0", 3) != 0) + { + grub_printf ("asn1_get_bit_der example long form\n"); + return 1; +@@ -211,7 +211,7 @@ test_simple (void) + + der_len = sizeof (der); + asn1_bit_der (str, bit_len, der, &der_len); +- if (der_len != 5 || memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0) ++ if (der_len != 5 || grub_memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0) + { + grub_printf ("asn1_bit_der example roundtrip\n"); + return 1; +diff --git a/grub-core/tests/asn1/tests/Test_strings.c b/grub-core/tests/asn1/tests/Test_strings.c +index 65c30937f..c7c1afa1b 100644 +--- a/grub-core/tests/asn1/tests/Test_strings.c ++++ b/grub-core/tests/asn1/tests/Test_strings.c +@@ -94,7 +94,7 @@ test_strings (void) + } + der_len = tl_len + tv[i].str_len; + +- if (der_len != tv[i].der_len || memcmp (tl, tv[i].der, tl_len) != 0) ++ if (der_len != tv[i].der_len || grub_memcmp (tl, tv[i].der, tl_len) != 0) + { + grub_printf ("DER encoding differs in %u! (size: %u, expected: %u)\n", i, der_len, tv[i].der_len); + return 1; +@@ -110,7 +110,7 @@ test_strings (void) + return 1; + } + +- if (str_len != tv[i].str_len || memcmp (str, tv[i].str, str_len) != 0) ++ if (str_len != tv[i].str_len || grub_memcmp (str, tv[i].str, str_len) != 0) + { + grub_printf ("DER decoded data differ in %u! (size: %u, expected: %u)\n", i, der_len, tv[i].str_len); + return 1; +@@ -130,12 +130,12 @@ test_strings (void) + return 1; + } + +- if (str_len != ber[i].str_len || memcmp (b, ber[i].str, str_len) != 0) ++ if (str_len != ber[i].str_len || grub_memcmp (b, ber[i].str, str_len) != 0) + { + grub_printf ("BER decoded data differ in %u! (size: %u, expected: %u)\n", i, str_len, ber[i].str_len); + return 1; + } +- free (b); ++ grub_free (b); + } + + +diff --git a/grub-core/tests/asn1/tests/object-id-decoding.c b/grub-core/tests/asn1/tests/object-id-decoding.c +index c087b46e3..91af8cde5 100644 +--- a/grub-core/tests/asn1/tests/object-id-decoding.c ++++ b/grub-core/tests/asn1/tests/object-id-decoding.c +@@ -74,7 +74,7 @@ test_object_id_decoding (void) + { + char str[128]; + int ret, ret_len; +- size_t i; ++ grub_size_t i; + + for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) + { +@@ -100,7 +100,7 @@ test_object_id_decoding (void) + return 1; + } + +- if (strcmp (tv[i].oid, str) != 0) ++ if (grub_strcmp (tv[i].oid, str) != 0) + { + grub_printf ("%d: strcmp iter %lu: got invalid OID: %s, expected: %s\n", + __LINE__, (unsigned long) i, str, tv[i].oid); +diff --git a/grub-core/tests/asn1/tests/object-id-encoding.c b/grub-core/tests/asn1/tests/object-id-encoding.c +index e3da092cc..f8f98ff17 100644 +--- a/grub-core/tests/asn1/tests/object-id-encoding.c ++++ b/grub-core/tests/asn1/tests/object-id-encoding.c +@@ -104,7 +104,7 @@ test_object_id_encoding (void) + return 1; + } + +- if (der_len != tv[i].der_len || memcmp (der, tv[i].der, der_len) != 0) ++ if (der_len != tv[i].der_len || grub_memcmp (der, tv[i].der, der_len) != 0) + { + grub_printf ("%d: iter %lu, re-encoding of OID %s resulted to different string (%d vs %d bytes)\n", + __LINE__, (unsigned long) i, tv[i].oid, der_len, tv[i].der_len); +diff --git a/grub-core/tests/asn1/tests/octet-string.c b/grub-core/tests/asn1/tests/octet-string.c +index d3a35dff8..dcf0fb808 100644 +--- a/grub-core/tests/asn1/tests/octet-string.c ++++ b/grub-core/tests/asn1/tests/octet-string.c +@@ -116,7 +116,7 @@ test_octet_string (void) + int str_size = sizeof (str); + unsigned char *tmp = NULL; + int ret, ret_len, j; +- size_t i; ++ grub_size_t i; + + for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) + { +@@ -146,7 +146,7 @@ test_octet_string (void) + } + + if (str_size != tv[i].len +- || memcmp (tv[i].string, str, tv[i].len) != 0) ++ || grub_memcmp (tv[i].string, str, tv[i].len) != 0) + { + grub_printf ("%d: memcmp: %s: got invalid decoding\n", + __LINE__, tv[i].name); +@@ -167,7 +167,7 @@ test_octet_string (void) + asn1_octet_der (str, str_size, der, &der_len); + + if (der_len != tv[i].der_len - 1 +- || memcmp (tv[i].der_str + 1, der, tv[i].der_len - 1) != 0) ++ || grub_memcmp (tv[i].der_str + 1, der, tv[i].der_len - 1) != 0) + { + grub_printf ("encoding: %s: got invalid encoding\n", tv[i].name); + return 1; +@@ -196,7 +196,7 @@ test_octet_string (void) + return 1; + } + +- if (str_size != tv[i].len || memcmp (tv[i].string, tmp, tv[i].len) != 0) ++ if (str_size != tv[i].len || grub_memcmp (tv[i].string, tmp, tv[i].len) != 0) + { + grub_printf ("%d: memcmp: %s: got invalid decoding\n", __LINE__, tv[i].name); + grub_printf ("\nGot:\t\t"); +@@ -209,7 +209,7 @@ test_octet_string (void) + grub_printf ("\n"); + return 1; + } +- free (tmp); ++ grub_free (tmp); + tmp = NULL; + + } +-- +2.43.0 + From 8a0fedef21aa882cb70f3eb9ae143589701cbc6d Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:34:43 +0800 Subject: [PATCH 224/402] asn1_test: Enable the testcase only when GRUB_LONG_MAX is larger than GRUB_INT_MAX There is a testcase to test the values larger than "int" but smaller than "long". However, for some architectures, "long" and "int" are the same and the compiler may issue a warning like this: grub-core/tests/asn1/tests/Test_overflow.c:48:50: error: left shift of negative value [-Werror=shift-negative-value] unsigned long num = ((long) GRUB_UINT_MAX) << 2; ^~ To avoid unnecessary error the testcase is enabled only when GRUB_LONG_MAX is larger than GRUB_INT_MAX. Signed-off-by: Daniel Axtens Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- ...-the-testcase-only-when-GRUB_LONG_MA.patch | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 grub-core/lib/libtasn1-patches/0013-asn1_test-enable-the-testcase-only-when-GRUB_LONG_MA.patch diff --git a/grub-core/lib/libtasn1-patches/0013-asn1_test-enable-the-testcase-only-when-GRUB_LONG_MA.patch b/grub-core/lib/libtasn1-patches/0013-asn1_test-enable-the-testcase-only-when-GRUB_LONG_MA.patch new file mode 100644 index 000000000..a81a350c9 --- /dev/null +++ b/grub-core/lib/libtasn1-patches/0013-asn1_test-enable-the-testcase-only-when-GRUB_LONG_MA.patch @@ -0,0 +1,48 @@ +From 6d308d9053447dcc003a9ca740c47c00932d3569 Mon Sep 17 00:00:00 2001 +From: Gary Lin +Date: Mon, 7 Oct 2024 11:33:19 +0800 +Subject: [PATCH 13/13] asn1_test: enable the testcase only when GRUB_LONG_MAX + is larger than GRUB_INT_MAX + +There is a testcase to test the values larger than 'int' but smaller +than 'long'. However, for some architectures, 'long' and 'int' are the +same, and the compiler may issue a warning like this: + +grub-core/tests/asn1/tests/Test_overflow.c:48:50: error: left shift of negative value [-Werror=shift-negative-value] + unsigned long num = ((long) GRUB_UINT_MAX) << 2; + ^~ + +To avoid unnecessary error the testcase is enabled only when +GRUB_LONG_MAX is larger than GRUB_INT_MAX. + +Signed-off-by: Daniel Axtens +Signed-off-by: Gary Lin +Reviewed-by: Daniel Kiper +--- + grub-core/tests/asn1/tests/Test_overflow.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/grub-core/tests/asn1/tests/Test_overflow.c b/grub-core/tests/asn1/tests/Test_overflow.c +index 65843abf6..fdeff5634 100644 +--- a/grub-core/tests/asn1/tests/Test_overflow.c ++++ b/grub-core/tests/asn1/tests/Test_overflow.c +@@ -43,7 +43,7 @@ test_overflow (void) + + /* Test that values larger than int but smaller than long are + rejected. This limitation was introduced with libtasn1 2.12. */ +- if (GRUB_LONG_MAX > GRUB_INT_MAX) ++#if (GRUB_LONG_MAX > GRUB_INT_MAX) + { + unsigned long num = ((long) GRUB_UINT_MAX) << 2; + unsigned char der[20]; +@@ -61,6 +61,7 @@ test_overflow (void) + return 1; + } + } ++#endif + + /* Test that values larger than would fit in the input string are + rejected. This problem was fixed in libtasn1 2.12. */ +-- +2.43.0 + From 504058e82a2d03351198acdde34027e756b42f38 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 15 Nov 2024 15:34:44 +0800 Subject: [PATCH 225/402] libtasn1: Compile into asn1 module Create a wrapper file that specifies the module license. Set up the makefile so it is built. Signed-off-by: Daniel Axtens Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- autogen.sh | 19 +++++++++++++++++++ grub-core/Makefile.core.def | 15 +++++++++++++++ grub-core/lib/libtasn1_wrap/wrap.c | 27 +++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 grub-core/lib/libtasn1_wrap/wrap.c diff --git a/autogen.sh b/autogen.sh index 195daa541..d08dc9a31 100755 --- a/autogen.sh +++ b/autogen.sh @@ -51,6 +51,25 @@ 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 +echo "Importing libtasn1..." +if [ -d grub-core/lib/libtasn1-grub ]; then + rm -rf grub-core/lib/libtasn1-grub +fi + +mkdir -p grub-core/lib/libtasn1-grub/lib +cp grub-core/lib/libtasn1/lib/*.[ch] grub-core/lib/libtasn1-grub/lib +cp grub-core/lib/libtasn1/libtasn1.h grub-core/lib/libtasn1-grub/ + +for patch in \ + 0001-libtasn1-disable-code-not-needed-in-grub.patch \ + 0002-libtasn1-replace-strcat-with-strcpy-in-_asn1_str_cat.patch \ + 0003-libtasn1-replace-strcat-with-_asn1_str_cat.patch \ + 0004-libtasn1-adjust-the-header-paths-in-libtasn1.h.patch \ + 0005-libtasn1-Use-grub_divmod64-for-division.patch \ + 0006-libtasn1-fix-the-potential-buffer-overrun.patch ; do + patch -p1 -i grub-core/lib/libtasn1-patches/$patch +done + echo "Generating Automake input..." # Automake doesn't like including files from a path outside the project. diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 063ef5dd7..cb4c5800e 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -2606,3 +2606,18 @@ module = { enable = efi; depends = part_gpt; }; + +module = { + name = asn1; + common = lib/libtasn1-grub/lib/decoding.c; + common = lib/libtasn1-grub/lib/coding.c; + common = lib/libtasn1-grub/lib/element.c; + common = lib/libtasn1-grub/lib/structure.c; + common = lib/libtasn1-grub/lib/parser_aux.c; + common = lib/libtasn1-grub/lib/gstr.c; + common = lib/libtasn1-grub/lib/errors.c; + common = lib/libtasn1_wrap/wrap.c; + cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)'; + /* -Wno-type-limits comes from configure.ac of libtasn1 */ + cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) -I$(srcdir)/lib/libtasn1-grub -I$(srcdir)/lib/libtasn1-grub/lib -Wno-type-limits'; +}; diff --git a/grub-core/lib/libtasn1_wrap/wrap.c b/grub-core/lib/libtasn1_wrap/wrap.c new file mode 100644 index 000000000..fcada55a8 --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/wrap.c @@ -0,0 +1,27 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 IBM Corporation + * Copyright (C) 2024 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 + +/* + * libtasn1 is provided under LGPL2.1+, which is compatible + * with GPL3+. As GRUB as a whole is under GPL3+, this module + * is therefore under GPL3+ also. + */ +GRUB_MOD_LICENSE ("GPLv3+"); From 99cda67889ffdd3ab9fcd447a6a9a48ec520a100 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 15 Nov 2024 15:34:45 +0800 Subject: [PATCH 226/402] asn1_test: Test module for libtasn1 Import tests from libtasn1 that use functionality we import. This test module is integrated into functional_test so that the user can run the test in GRUB shell. This doesn't test the full decoder but that will be exercised in test suites for coming patch sets. Add testcase target in accordance with commit 5e10be48e5 (tests: Add check-native and check-nonnative make targets). Cc: Vladimir Serbinenko Signed-off-by: Daniel Axtens Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- Makefile.util.def | 6 ++++ autogen.sh | 16 ++++++++- grub-core/Makefile.core.def | 15 ++++++++ grub-core/tests/asn1/asn1_test.c | 50 +++++++++++++++++++++++++++ grub-core/tests/asn1/asn1_test.h | 45 ++++++++++++++++++++++++ grub-core/tests/lib/functional_test.c | 1 + tests/asn1_test.in | 11 ++++++ 7 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 grub-core/tests/asn1/asn1_test.c create mode 100644 grub-core/tests/asn1/asn1_test.h create mode 100644 tests/asn1_test.in diff --git a/Makefile.util.def b/Makefile.util.def index 0f74a1680..fe70cf9bd 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -1257,6 +1257,12 @@ script = { common = tests/luks2_test.in; }; +script = { + testcase = native; + name = asn1_test; + common = tests/asn1_test.in; +}; + program = { testcase = native; name = example_unit_test; diff --git a/autogen.sh b/autogen.sh index d08dc9a31..ebd614792 100755 --- a/autogen.sh +++ b/autogen.sh @@ -60,13 +60,27 @@ mkdir -p grub-core/lib/libtasn1-grub/lib cp grub-core/lib/libtasn1/lib/*.[ch] grub-core/lib/libtasn1-grub/lib cp grub-core/lib/libtasn1/libtasn1.h grub-core/lib/libtasn1-grub/ +if [ -d grub-core/tests/asn1/tests ]; then + rm -rf grub-core/tests/asn1/tests +fi + +mkdir grub-core/tests/asn1/tests +cp grub-core/lib/libtasn1/tests/*.[ch] grub-core/tests/asn1/tests + for patch in \ 0001-libtasn1-disable-code-not-needed-in-grub.patch \ 0002-libtasn1-replace-strcat-with-strcpy-in-_asn1_str_cat.patch \ 0003-libtasn1-replace-strcat-with-_asn1_str_cat.patch \ 0004-libtasn1-adjust-the-header-paths-in-libtasn1.h.patch \ 0005-libtasn1-Use-grub_divmod64-for-division.patch \ - 0006-libtasn1-fix-the-potential-buffer-overrun.patch ; do + 0006-libtasn1-fix-the-potential-buffer-overrun.patch \ + 0007-asn1_test-include-asn1_test.h-only.patch \ + 0008-asn1_test-rename-the-main-functions-to-the-test-name.patch \ + 0009-asn1_test-return-either-0-or-1-to-reflect-the-result.patch \ + 0010-asn1_test-remove-verbose-and-the-unnecessary-printf.patch \ + 0011-asn1_test-print-the-error-messages-with-grub_printf.patch \ + 0012-asn1_test-use-the-grub-specific-functions-and-types.patch \ + 0013-asn1_test-enable-the-testcase-only-when-GRUB_LONG_MA.patch ; do patch -p1 -i grub-core/lib/libtasn1-patches/$patch done diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index cb4c5800e..a38955e18 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -2621,3 +2621,18 @@ module = { /* -Wno-type-limits comes from configure.ac of libtasn1 */ cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) -I$(srcdir)/lib/libtasn1-grub -I$(srcdir)/lib/libtasn1-grub/lib -Wno-type-limits'; }; + +module = { + name = asn1_test; + common = tests/asn1/tests/CVE-2018-1000654.c; + common = tests/asn1/tests/object-id-decoding.c; + common = tests/asn1/tests/object-id-encoding.c; + common = tests/asn1/tests/octet-string.c; + common = tests/asn1/tests/reproducers.c; + common = tests/asn1/tests/Test_overflow.c; + common = tests/asn1/tests/Test_simple.c; + common = tests/asn1/tests/Test_strings.c; + common = tests/asn1/asn1_test.c; + cflags = '-Wno-uninitialized'; + cppflags = '-I$(srcdir)/lib/libtasn1-grub -I$(srcdir)/tests/asn1/'; +}; diff --git a/grub-core/tests/asn1/asn1_test.c b/grub-core/tests/asn1/asn1_test.c new file mode 100644 index 000000000..69606b004 --- /dev/null +++ b/grub-core/tests/asn1/asn1_test.c @@ -0,0 +1,50 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 IBM Corporation + * Copyright (C) 2024 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 "asn1_test.h" + +/* + * libtasn1 tests - from which this is derived - are provided under GPL3+. + */ +GRUB_MOD_LICENSE ("GPLv3+"); + +static void +asn1_test (void) +{ + grub_test_assert (test_CVE_2018_1000654 () == 0, "CVE-2018-1000654 test failed"); + + grub_test_assert (test_object_id_encoding () == 0, "ASN.1 object ID encoding test failed"); + + grub_test_assert (test_object_id_decoding () == 0, "ASN.1 object ID decoding test failed"); + + grub_test_assert (test_octet_string () == 0, "ASN.1 octet string test failed"); + + grub_test_assert (test_overflow () == 0, "ASN.1 overflow test failed"); + + grub_test_assert (test_reproducers () == 0, "ASN.1 reproducers test failed"); + + grub_test_assert (test_simple () == 0, "ASN.1 simple test failed"); + + grub_test_assert (test_strings () == 0, "ASN.1 strings test fail" ); +} + +/* Register asn1_test method as a functional test. */ +GRUB_FUNCTIONAL_TEST (asn1_test, asn1_test); diff --git a/grub-core/tests/asn1/asn1_test.h b/grub-core/tests/asn1/asn1_test.h new file mode 100644 index 000000000..8e83d70f5 --- /dev/null +++ b/grub-core/tests/asn1/asn1_test.h @@ -0,0 +1,45 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 IBM Corporation + * Copyright (C) 2024 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 LIBTASN1_WRAP_TESTS_H +#define LIBTASN1_WRAP_TESTS_H + +#include +#include +#include +#include +#include + +extern int test_CVE_2018_1000654 (void); + +extern int test_object_id_encoding (void); + +extern int test_object_id_decoding (void); + +extern int test_octet_string (void); + +extern int test_overflow (void); + +extern int test_reproducers (void); + +extern int test_simple (void); + +extern int test_strings (void); + +#endif diff --git a/grub-core/tests/lib/functional_test.c b/grub-core/tests/lib/functional_test.c index 96781fb39..38e981f2c 100644 --- a/grub-core/tests/lib/functional_test.c +++ b/grub-core/tests/lib/functional_test.c @@ -79,6 +79,7 @@ grub_functional_all_tests (grub_extcmd_context_t ctxt __attribute__ ((unused)), grub_dl_load ("cmp_test"); grub_dl_load ("mul_test"); grub_dl_load ("shift_test"); + grub_dl_load ("asn1_test"); FOR_LIST_ELEMENTS (test, grub_test_list) ok = !grub_test_run (test) && ok; diff --git a/tests/asn1_test.in b/tests/asn1_test.in new file mode 100644 index 000000000..8f18ee6bb --- /dev/null +++ b/tests/asn1_test.in @@ -0,0 +1,11 @@ +#! @BUILD_SHEBANG@ +set -e + +. "@builddir@/grub-core/modinfo.sh" + +out=`echo functional_test asn1_test | @builddir@/grub-shell` + +if [ "$(echo "$out" | tail -n 1)" != "ALL TESTS PASSED" ]; then + echo "ASN.1 test failure: $out" + exit 1 +fi From 3d60732f9febd938ee1078315f23509c2c0e3cdb Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:34:46 +0800 Subject: [PATCH 227/402] libtasn1: Add the documentation Document libtasn1 in docs/grub-dev.texi and add the upgrade steps. Also add the patches to make libtasn1 compatible with GRUB code. Signed-off-by: Gary Lin Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- docs/grub-dev.texi | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi index 1276c5930..3ad8e3efa 100644 --- a/docs/grub-dev.texi +++ b/docs/grub-dev.texi @@ -506,6 +506,7 @@ to update it. * Gnulib:: * jsmn:: * minilzo:: +* libtasn1:: @end menu @node Gnulib @@ -596,6 +597,40 @@ cp minilzo-2.10/*.[hc] grub-core/lib/minilzo rm -r minilzo-2.10* @end example +@node libtasn1 +@section libtasn1 + +libtasn1 is a library providing Abstract Syntax Notation One (ASN.1, as +specified by the X.680 ITU-T recommendation) parsing and structures management, +and Distinguished Encoding Rules (DER, as per X.690) encoding and decoding +functions. + +To upgrade to a new version of the libtasn1 library, download the release +tarball and copy the files into the target directory: + +@example +curl -L -O https://ftp.gnu.org/gnu/libtasn1/libtasn1-4.19.0.tar.gz +tar xvzf libtasn1-4.19.0.tar.gz +rm -rf grub-core/lib/libtasn1 +mkdir -p grub-core/lib/libtasn1/lib +mkdir -p grub-core/lib/libtasn1/tests +cp libtasn1-4.19.0/@lbracechar{}README.md,COPYING@rbracechar{} grub-core/lib/libtasn1 +cp libtasn1-4.19.0/lib/@lbracechar{}coding.c,decoding.c,element.c,element.h,errors.c,gstr.c,gstr.h,int.h,parser_aux.c,parser_aux.h,structure.c,structure.h@rbracechar{} grub-core/lib/libtasn1/lib +cp libtasn1-4.19.0/lib/includes/libtasn1.h grub-core/lib/libtasn1 +cp libtasn1-4.19.0/tests/@lbracechar{}CVE-2018-1000654-1_asn1_tab.h,CVE-2018-1000654-2_asn1_tab.h,CVE-2018-1000654.c,object-id-decoding.c,object-id-encoding.c,octet-string.c,reproducers.c,Test_overflow.c,Test_simple.c,Test_strings.c@rbracechar{} grub-core/lib/libtasn1/tests +rm -rf libtasn1-4.19.0* +@end example + +After upgrading the library, it may be necessary to apply the patches in +@file{grub-core/lib/libtasn1-patches/} to adjust the code to be compatible with +GRUB. These patches were needed to use the current version of libtasn1. The +existing patches may not apply cleanly, apply at all, or even be needed for a +newer version of the library, and other patches may be needed due to changes in +the newer version. If existing patches need to be refreshed to apply cleanly, +please include updated patches as part of the a patch set sent to the list. +If new patches are needed or existing patches are not needed, also please send +additions or removals as part of any patch set upgrading libtasn1. + @node Debugging @chapter Debugging From 5d260302da672258444b01239803c8f4d753e3f3 Mon Sep 17 00:00:00 2001 From: Hernan Gatta Date: Fri, 15 Nov 2024 15:34:47 +0800 Subject: [PATCH 228/402] key_protector: Add key protectors framework A key protector encapsulates functionality to retrieve an unlocking key for a fully-encrypted disk from a specific source. A key protector module registers itself with the key protectors framework when it is loaded and unregisters when unloaded. Additionally, a key protector may accept parameters that describe how it should operate. The key protectors framework, besides offering registration and unregistration functions, also offers a one-stop routine for finding and invoking a key protector by name. If a key protector with the specified name exists and if an unlocking key is successfully retrieved by it, the function returns to the caller the retrieved key and its length. Cc: Vladimir Serbinenko Signed-off-by: Hernan Gatta Signed-off-by: Gary Lin Reviewed-by: Stefan Berger Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- grub-core/Makefile.am | 1 + grub-core/Makefile.core.def | 5 +++ grub-core/disk/key_protector.c | 73 ++++++++++++++++++++++++++++++++++ include/grub/key_protector.h | 47 ++++++++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 grub-core/disk/key_protector.c create mode 100644 include/grub/key_protector.h diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index 1eda467e0..e50db8106 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -90,6 +90,7 @@ endif KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/mm.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/parser.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/partition.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/key_protector.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/stack_protector.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/term.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/time.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index a38955e18..37f131ae2 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1282,6 +1282,11 @@ module = { common = disk/raid6_recover.c; }; +module = { + name = key_protector; + common = disk/key_protector.c; +}; + module = { name = scsi; common = disk/scsi.c; diff --git a/grub-core/disk/key_protector.c b/grub-core/disk/key_protector.c new file mode 100644 index 000000000..0d146c1c0 --- /dev/null +++ b/grub-core/disk/key_protector.c @@ -0,0 +1,73 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct grub_key_protector *grub_key_protectors = NULL; + +grub_err_t +grub_key_protector_register (struct grub_key_protector *protector) +{ + if (protector == NULL || protector->name == NULL || protector->name[0] == '\0') + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid key protector for registration"); + + if (grub_key_protectors != NULL && + grub_named_list_find (GRUB_AS_NAMED_LIST (grub_key_protectors), protector->name) != NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Key protector '%s' already registered", protector->name); + + grub_list_push (GRUB_AS_LIST_P (&grub_key_protectors), GRUB_AS_LIST (protector)); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_key_protector_unregister (struct grub_key_protector *protector) +{ + if (protector == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid key protector for unregistration"); + + grub_list_remove (GRUB_AS_LIST (protector)); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_key_protector_recover_key (const char *protector, grub_uint8_t **key, + grub_size_t *key_size) +{ + struct grub_key_protector *kp = NULL; + + if (grub_key_protectors == NULL) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "No key protector registered"); + + if (protector == NULL || protector[0] == '\0') + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid key protector"); + + kp = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_key_protectors), protector); + if (kp == NULL) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "Key protector '%s' not found", protector); + + return kp->recover_key (key, key_size); +} diff --git a/include/grub/key_protector.h b/include/grub/key_protector.h new file mode 100644 index 000000000..00b15c13d --- /dev/null +++ b/include/grub/key_protector.h @@ -0,0 +1,47 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 GRUB_PROTECTOR_HEADER +#define GRUB_PROTECTOR_HEADER 1 + +#include +#include + +struct grub_key_protector +{ + struct grub_key_protector *next; + struct grub_key_protector **prev; + + const char *name; + + grub_err_t (*recover_key) (grub_uint8_t **key, grub_size_t *key_size); +}; + +grub_err_t +grub_key_protector_register (struct grub_key_protector *protector); + +grub_err_t +grub_key_protector_unregister (struct grub_key_protector *protector); + +grub_err_t +grub_key_protector_recover_key (const char *protector, + grub_uint8_t **key, + grub_size_t *key_size); + +#endif /* ! GRUB_PROTECTOR_HEADER */ From 2ad159d9b34324db445860ee5bcfe83cbeed6f6d Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:34:48 +0800 Subject: [PATCH 229/402] tss2: Add TPM2 buffer handling functions As the preparation to support TPM2 Software Stack (TSS2), this commit implements the TPM2 buffer handling functions to pack data for the TPM2 commands and unpack the data from the response. Cc: Stefan Berger Signed-off-by: Hernan Gatta Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- grub-core/lib/tss2/buffer.c | 147 +++++++++++++++++++++++++++++++ grub-core/lib/tss2/tss2_buffer.h | 64 ++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 grub-core/lib/tss2/buffer.c create mode 100644 grub-core/lib/tss2/tss2_buffer.h diff --git a/grub-core/lib/tss2/buffer.c b/grub-core/lib/tss2/buffer.c new file mode 100644 index 000000000..16d59a8f5 --- /dev/null +++ b/grub-core/lib/tss2/buffer.c @@ -0,0 +1,147 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 + +void grub_tpm2_buffer_init (grub_tpm2_buffer_t buffer) +{ + grub_memset (buffer->data, 0, sizeof (buffer->data)); + buffer->size = 0; + buffer->offset = 0; + buffer->cap = sizeof (buffer->data); + buffer->error = 0; +} + +void +grub_tpm2_buffer_pack (grub_tpm2_buffer_t buffer, const void *data, grub_size_t size) +{ + grub_uint32_t r = buffer->cap - buffer->size; + + if (buffer->error) + return; + + if (size > r) + { + buffer->error = 1; + return; + } + + grub_memcpy (&buffer->data[buffer->size], (void *) data, size); + buffer->size += size; +} + +void +grub_tpm2_buffer_pack_u8 (grub_tpm2_buffer_t buffer, grub_uint8_t value) +{ + grub_tpm2_buffer_pack (buffer, (const void *) &value, sizeof (value)); +} + +void +grub_tpm2_buffer_pack_u16 (grub_tpm2_buffer_t buffer, grub_uint16_t value) +{ + grub_uint16_t tmp = grub_cpu_to_be16 (value); + + grub_tpm2_buffer_pack (buffer, (const void *) &tmp, sizeof (tmp)); +} + +void +grub_tpm2_buffer_pack_u32 (grub_tpm2_buffer_t buffer, grub_uint32_t value) +{ + grub_uint32_t tmp = grub_cpu_to_be32 (value); + + grub_tpm2_buffer_pack (buffer, (const void *) &tmp, sizeof (tmp)); +} + +void +grub_tpm2_buffer_unpack (grub_tpm2_buffer_t buffer, void *data, grub_size_t size) +{ + grub_uint32_t r = buffer->size - buffer->offset; + + if (buffer->error) + return; + + if (size > r) + { + buffer->error = 1; + return; + } + + grub_memcpy (data, &buffer->data[buffer->offset], size); + buffer->offset += size; +} + +void +grub_tpm2_buffer_unpack_u8 (grub_tpm2_buffer_t buffer, grub_uint8_t *value) +{ + grub_uint32_t r = buffer->size - buffer->offset; + + if (buffer->error) + return; + + if (sizeof (*value) > r) + { + buffer->error = 1; + return; + } + + grub_memcpy (value, &buffer->data[buffer->offset], sizeof (*value)); + buffer->offset += sizeof (*value); +} + +void +grub_tpm2_buffer_unpack_u16 (grub_tpm2_buffer_t buffer, grub_uint16_t *value) +{ + grub_uint16_t tmp; + grub_uint32_t r = buffer->size - buffer->offset; + + if (buffer->error) + return; + + if (sizeof (tmp) > r) + { + buffer->error = 1; + return; + } + + grub_memcpy (&tmp, &buffer->data[buffer->offset], sizeof (tmp)); + buffer->offset += sizeof (tmp); + *value = grub_be_to_cpu16 (tmp); +} + +void +grub_tpm2_buffer_unpack_u32 (grub_tpm2_buffer_t buffer, grub_uint32_t *value) +{ + grub_uint32_t tmp; + grub_uint32_t r = buffer->size - buffer->offset; + + if (buffer->error) + return; + + if (sizeof (tmp) > r) + { + buffer->error = 1; + return; + } + + grub_memcpy (&tmp, &buffer->data[buffer->offset], sizeof (tmp)); + buffer->offset += sizeof (tmp); + *value = grub_be_to_cpu32 (tmp); +} diff --git a/grub-core/lib/tss2/tss2_buffer.h b/grub-core/lib/tss2/tss2_buffer.h new file mode 100644 index 000000000..fb9db1aed --- /dev/null +++ b/grub-core/lib/tss2/tss2_buffer.h @@ -0,0 +1,64 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 GRUB_TPM2_BUFFER_HEADER +#define GRUB_TPM2_BUFFER_HEADER 1 + +#include + +#define GRUB_TPM2_BUFFER_CAPACITY 4096 + +struct grub_tpm2_buffer +{ + grub_uint8_t data[GRUB_TPM2_BUFFER_CAPACITY]; + grub_size_t size; + grub_size_t offset; + grub_size_t cap; + bool error; +}; +typedef struct grub_tpm2_buffer *grub_tpm2_buffer_t; + +extern void +grub_tpm2_buffer_init (grub_tpm2_buffer_t buffer); + +extern void +grub_tpm2_buffer_pack (grub_tpm2_buffer_t buffer, const void *data, grub_size_t size); + +extern void +grub_tpm2_buffer_pack_u8 (grub_tpm2_buffer_t buffer, grub_uint8_t value); + +extern void +grub_tpm2_buffer_pack_u16 (grub_tpm2_buffer_t buffer, grub_uint16_t value); + +extern void +grub_tpm2_buffer_pack_u32 (grub_tpm2_buffer_t buffer, grub_uint32_t value); + +extern void +grub_tpm2_buffer_unpack (grub_tpm2_buffer_t buffer, void *data, grub_size_t size); + +extern void +grub_tpm2_buffer_unpack_u8 (grub_tpm2_buffer_t buffer, grub_uint8_t *value); + +extern void +grub_tpm2_buffer_unpack_u16 (grub_tpm2_buffer_t buffer, grub_uint16_t *value); + +extern void +grub_tpm2_buffer_unpack_u32 (grub_tpm2_buffer_t buffer, grub_uint32_t *value); + +#endif /* ! GRUB_TPM2_BUFFER_HEADER */ From 63a78f4b4dfa339f5cb68ecadb261639c9ca3cf8 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:34:49 +0800 Subject: [PATCH 230/402] tss2: Add TPM2 types and Marshal/Unmarshal functions This commit adds the necessary TPM2 types and structs as the preparation for the TPM2 Software Stack (TSS2) support. The Marshal/Unmarshal functions are also added to handle the data structure to be submitted to TPM2 commands and to be received from the response. Signed-off-by: Hernan Gatta Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Reviewed-by: Stefan Berger Tested-by: Stefan Berger --- grub-core/lib/tss2/tss2_mu.c | 1174 +++++++++++++++++++++++++++++ grub-core/lib/tss2/tss2_mu.h | 397 ++++++++++ grub-core/lib/tss2/tss2_structs.h | 796 +++++++++++++++++++ grub-core/lib/tss2/tss2_types.h | 404 ++++++++++ 4 files changed, 2771 insertions(+) create mode 100644 grub-core/lib/tss2/tss2_mu.c create mode 100644 grub-core/lib/tss2/tss2_mu.h create mode 100644 grub-core/lib/tss2/tss2_structs.h create mode 100644 grub-core/lib/tss2/tss2_types.h diff --git a/grub-core/lib/tss2/tss2_mu.c b/grub-core/lib/tss2/tss2_mu.c new file mode 100644 index 000000000..86134cc0a --- /dev/null +++ b/grub-core/lib/tss2/tss2_mu.c @@ -0,0 +1,1174 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 + +void +grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_AUTH_COMMAND_t *authCommand) +{ + grub_uint32_t start; + grub_uint32_t tmp; + + grub_tpm2_buffer_pack_u32 (buffer, 0); + start = buffer->size; + + grub_tpm2_buffer_pack_u32 (buffer, authCommand->sessionHandle); + + grub_tpm2_buffer_pack_u16 (buffer, authCommand->nonce.size); + grub_tpm2_buffer_pack (buffer, authCommand->nonce.buffer, authCommand->nonce.size); + + grub_tpm2_buffer_pack_u8 (buffer, *((const grub_uint8_t *) &authCommand->sessionAttributes)); + + grub_tpm2_buffer_pack_u16 (buffer, authCommand->hmac.size); + grub_tpm2_buffer_pack (buffer, authCommand->hmac.buffer, authCommand->hmac.size); + + tmp = grub_cpu_to_be32 (buffer->size - start); + grub_memcpy (&buffer->data[start - sizeof (grub_uint32_t)], &tmp, sizeof (tmp)); +} + +void +grub_Tss2_MU_TPM2B_Marshal (grub_tpm2_buffer_t buffer, + const grub_uint16_t size, + const grub_uint8_t *b) +{ + grub_uint16_t i; + + grub_tpm2_buffer_pack_u16 (buffer, size); + + for (i = 0; i < size; i++) + grub_tpm2_buffer_pack_u8 (buffer, b[i]); +} + +void +grub_Tss2_MU_TPMU_SYM_KEY_BITS_Marshal (grub_tpm2_buffer_t buffer, + const TPMI_ALG_SYM_OBJECT_t algorithm, + const TPMU_SYM_KEY_BITS_t *p) +{ + switch (algorithm) + { + case TPM_ALG_AES: + case TPM_ALG_SM4: + case TPM_ALG_CAMELLIA: + case TPM_ALG_XOR: + grub_tpm2_buffer_pack_u16 (buffer, *((const grub_uint16_t *) p)); + break; + case TPM_ALG_NULL: + break; + default: + buffer->error = 1; + break; + } +} + +void +grub_Tss2_MU_TPMU_SYM_MODE_Marshal (grub_tpm2_buffer_t buffer, + const TPMI_ALG_SYM_OBJECT_t algorithm, + const TPMU_SYM_MODE_t *p) +{ + switch (algorithm) + { + case TPM_ALG_AES: + case TPM_ALG_SM4: + case TPM_ALG_CAMELLIA: + grub_tpm2_buffer_pack_u16 (buffer, *((const grub_uint16_t *) p)); + break; + case TPM_ALG_XOR: + case TPM_ALG_NULL: + break; + default: + buffer->error = 1; + break; + } +} + +void +grub_Tss2_MU_TPMT_SYM_DEF_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_SYM_DEF_t *p) +{ + grub_tpm2_buffer_pack_u16 (buffer, p->algorithm); + grub_Tss2_MU_TPMU_SYM_KEY_BITS_Marshal (buffer, p->algorithm, &p->keyBits); + grub_Tss2_MU_TPMU_SYM_MODE_Marshal (buffer, p->algorithm, &p->mode); +} + +void +grub_Tss2_MU_TPMS_PCR_SELECTION_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_PCR_SELECTION_t *pcrSelection) +{ + grub_uint32_t i; + + grub_tpm2_buffer_pack_u16 (buffer, pcrSelection->hash); + grub_tpm2_buffer_pack_u8 (buffer, pcrSelection->sizeOfSelect); + + for (i = 0; i < pcrSelection->sizeOfSelect; i++) + grub_tpm2_buffer_pack_u8 (buffer, pcrSelection->pcrSelect[i]); +} + +void +grub_Tss2_MU_TPML_PCR_SELECTION_Marshal (grub_tpm2_buffer_t buffer, + const TPML_PCR_SELECTION_t *pcrSelection) +{ + grub_uint32_t i; + + grub_tpm2_buffer_pack_u32 (buffer, pcrSelection->count); + + for (i = 0; i < pcrSelection->count; i++) + grub_Tss2_MU_TPMS_PCR_SELECTION_Marshal (buffer, &pcrSelection->pcrSelections[i]); +} + +void +grub_Tss2_MU_TPMA_OBJECT_Marshal (grub_tpm2_buffer_t buffer, + const TPMA_OBJECT_t *p) +{ + grub_tpm2_buffer_pack_u32 (buffer, *((const grub_uint32_t *) p)); +} + +void +grub_Tss2_MU_TPMS_SCHEME_XOR_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_SCHEME_XOR_t *p) +{ + grub_tpm2_buffer_pack_u16 (buffer, p->hashAlg); + grub_tpm2_buffer_pack_u16 (buffer, p->kdf); +} + +void +grub_Tss2_MU_TPMS_SCHEME_HMAC_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_SCHEME_HMAC_t *p) +{ + grub_tpm2_buffer_pack_u16 (buffer, p->hashAlg); +} + +void +grub_Tss2_MU_TPMU_SCHEME_KEYEDHASH_Marshal (grub_tpm2_buffer_t buffer, + const TPMI_ALG_KEYEDHASH_SCHEME_t scheme, + const TPMU_SCHEME_KEYEDHASH_t *p) +{ + switch (scheme) + { + case TPM_ALG_HMAC: + grub_Tss2_MU_TPMS_SCHEME_HMAC_Marshal (buffer, &p->hmac); + break; + case TPM_ALG_XOR: + grub_Tss2_MU_TPMS_SCHEME_XOR_Marshal (buffer, &p->exclusiveOr); + break; + case TPM_ALG_NULL: + break; + default: + buffer->error = 1; + break; + } +} + +void +grub_Tss2_MU_TPMT_KEYEDHASH_SCHEME_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_KEYEDHASH_SCHEME_t *p) +{ + grub_tpm2_buffer_pack_u16 (buffer, p->scheme); + grub_Tss2_MU_TPMU_SCHEME_KEYEDHASH_Marshal (buffer, p->scheme, &p->details); +} + +void +grub_Tss2_MU_TPMS_KEYEDHASH_PARMS_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_KEYEDHASH_PARMS_t *p) +{ + grub_Tss2_MU_TPMT_KEYEDHASH_SCHEME_Marshal (buffer, &p->scheme); +} + +void +grub_Tss2_MU_TPMT_SYM_DEF_OBJECT_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_SYM_DEF_OBJECT_t *p) +{ + grub_tpm2_buffer_pack_u16 (buffer, p->algorithm); + grub_Tss2_MU_TPMU_SYM_KEY_BITS_Marshal (buffer, p->algorithm, &p->keyBits); + grub_Tss2_MU_TPMU_SYM_MODE_Marshal (buffer, p->algorithm, &p->mode); +} + +void +grub_Tss2_MU_TPMU_ASYM_SCHEME_Marshal (grub_tpm2_buffer_t buffer, + const TPMI_ALG_RSA_DECRYPT_t scheme, + const TPMU_ASYM_SCHEME_t *p __attribute__ ((unused))) +{ + switch (scheme) + { + case TPM_ALG_NULL: + break; + default: + /* Unsupported */ + buffer->error = 1; + break; + } +} + +void +grub_Tss2_MU_TPMT_RSA_SCHEME_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_RSA_SCHEME_t *p) +{ + grub_tpm2_buffer_pack_u16 (buffer, p->scheme); + grub_Tss2_MU_TPMU_ASYM_SCHEME_Marshal (buffer, p->scheme, &p->details); +} + +void +grub_Tss2_MU_TPMS_RSA_PARMS_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_RSA_PARMS_t *p) +{ + grub_Tss2_MU_TPMT_SYM_DEF_OBJECT_Marshal (buffer, &p->symmetric); + grub_Tss2_MU_TPMT_RSA_SCHEME_Marshal (buffer, &p->scheme); + grub_tpm2_buffer_pack_u16 (buffer, p->keyBits); + grub_tpm2_buffer_pack_u32 (buffer, p->exponent); +} + +void +grub_Tss2_MU_TPMS_SYMCIPHER_PARMS_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_SYMCIPHER_PARMS_t *p) +{ + grub_Tss2_MU_TPMT_SYM_DEF_OBJECT_Marshal (buffer, &p->sym); +} + +void +grub_Tss2_MU_TPMT_ECC_SCHEME_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_ECC_SCHEME_t *p) +{ + grub_tpm2_buffer_pack_u16 (buffer, p->scheme); + grub_Tss2_MU_TPMU_ASYM_SCHEME_Marshal (buffer, p->scheme, &p->details); +} + +void +grub_Tss2_MU_TPMU_KDF_SCHEME_Marshal (grub_tpm2_buffer_t buffer, + const TPMI_ALG_KDF_t scheme, + const TPMU_KDF_SCHEME_t *p) +{ + switch (scheme) + { + case TPM_ALG_MGF1: + grub_tpm2_buffer_pack_u16 (buffer, p->mgf1.hashAlg); + break; + case TPM_ALG_KDF1_SP800_56A: + grub_tpm2_buffer_pack_u16 (buffer, p->kdf1_sp800_56a.hashAlg); + break; + case TPM_ALG_KDF2: + grub_tpm2_buffer_pack_u16 (buffer, p->kdf2.hashAlg); + break; + case TPM_ALG_KDF1_SP800_108: + grub_tpm2_buffer_pack_u16 (buffer, p->kdf1_sp800_108.hashAlg); + break; + case TPM_ALG_NULL: + break; + default: + buffer->error = 1; + break; + } +} + +void +grub_Tss2_MU_TPMT_KDF_SCHEME_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_KDF_SCHEME_t *p) +{ + grub_tpm2_buffer_pack_u16 (buffer, p->scheme); + grub_Tss2_MU_TPMU_KDF_SCHEME_Marshal (buffer, p->scheme, &p->details); +} + +void +grub_Tss2_MU_TPMS_ECC_PARMS_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_ECC_PARMS_t *p) +{ + grub_Tss2_MU_TPMT_SYM_DEF_OBJECT_Marshal (buffer, &p->symmetric); + grub_Tss2_MU_TPMT_ECC_SCHEME_Marshal (buffer, &p->scheme); + grub_tpm2_buffer_pack_u16 (buffer, p->curveID); + grub_Tss2_MU_TPMT_KDF_SCHEME_Marshal (buffer, &p->kdf); +} + +void +grub_Tss2_MU_TPMU_PUBLIC_PARMS_Marshal (grub_tpm2_buffer_t buffer, + const grub_uint32_t type, + const TPMU_PUBLIC_PARMS_t *p) +{ + switch (type) + { + case TPM_ALG_KEYEDHASH: + grub_Tss2_MU_TPMS_KEYEDHASH_PARMS_Marshal (buffer, &p->keyedHashDetail); + break; + case TPM_ALG_SYMCIPHER: + grub_Tss2_MU_TPMS_SYMCIPHER_PARMS_Marshal (buffer, &p->symDetail); + break; + case TPM_ALG_RSA: + grub_Tss2_MU_TPMS_RSA_PARMS_Marshal (buffer, &p->rsaDetail); + break; + case TPM_ALG_ECC: + grub_Tss2_MU_TPMS_ECC_PARMS_Marshal (buffer, &p->eccDetail); + break; + default: + buffer->error = 1; + break; + } +} + +void +grub_Tss2_MU_TPMS_ECC_POINT_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_ECC_POINT_t *p) +{ + grub_Tss2_MU_TPM2B_Marshal (buffer, p->x.size, p->x.buffer); + grub_Tss2_MU_TPM2B_Marshal (buffer, p->y.size, p->y.buffer); +} + +void +grub_Tss2_MU_TPMU_PUBLIC_ID_Marshal (grub_tpm2_buffer_t buffer, + const TPMI_ALG_PUBLIC_t type, + const TPMU_PUBLIC_ID_t *p) +{ + switch(type) + { + case TPM_ALG_KEYEDHASH: + grub_Tss2_MU_TPM2B_Marshal (buffer, p->keyedHash.size, p->keyedHash.buffer); + break; + case TPM_ALG_SYMCIPHER: + grub_Tss2_MU_TPM2B_Marshal (buffer, p->sym.size, p->sym.buffer); + break; + case TPM_ALG_RSA: + grub_Tss2_MU_TPM2B_Marshal (buffer, p->rsa.size, p->rsa.buffer); + break; + case TPM_ALG_ECC: + grub_Tss2_MU_TPMS_ECC_POINT_Marshal (buffer, &p->ecc); + break; + default: + buffer->error = 1; + break; + } +} + +void +grub_Tss2_MU_TPMT_PUBLIC_PARMS_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_PUBLIC_PARMS_t *p) +{ + grub_tpm2_buffer_pack_u16 (buffer, p->type); + grub_Tss2_MU_TPMU_PUBLIC_PARMS_Marshal (buffer, p->type, &p->parameters); +} + +void +grub_Tss2_MU_TPMT_PUBLIC_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_PUBLIC_t *p) +{ + grub_tpm2_buffer_pack_u16 (buffer, p->type); + grub_tpm2_buffer_pack_u16 (buffer, p->nameAlg); + grub_Tss2_MU_TPMA_OBJECT_Marshal (buffer, &p->objectAttributes); + grub_Tss2_MU_TPM2B_Marshal (buffer, p->authPolicy.size, p->authPolicy.buffer); + grub_Tss2_MU_TPMU_PUBLIC_PARMS_Marshal (buffer, p->type, &p->parameters); + grub_Tss2_MU_TPMU_PUBLIC_ID_Marshal (buffer, p->type, &p->unique); +} + +void +grub_Tss2_MU_TPM2B_PUBLIC_Marshal (grub_tpm2_buffer_t buffer, + const TPM2B_PUBLIC_t *p) +{ + grub_uint32_t start; + grub_uint16_t size; + + if (p) + { + grub_tpm2_buffer_pack_u16 (buffer, p->size); + + start = buffer->size; + grub_Tss2_MU_TPMT_PUBLIC_Marshal (buffer, &p->publicArea); + size = grub_cpu_to_be16 (buffer->size - start); + grub_memcpy (&buffer->data[start - sizeof (grub_uint16_t)], &size, sizeof (size)); + } + else + grub_tpm2_buffer_pack_u16 (buffer, 0); +} + +void +grub_Tss2_MU_TPMS_SENSITIVE_CREATE_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_SENSITIVE_CREATE_t *p) +{ + grub_Tss2_MU_TPM2B_Marshal (buffer, p->userAuth.size, p->userAuth.buffer); + grub_Tss2_MU_TPM2B_Marshal (buffer, p->data.size, p->data.buffer); +} + +void +grub_Tss2_MU_TPMU_SENSITIVE_COMPOSITE_Marshal (grub_tpm2_buffer_t buffer, + const TPMI_ALG_PUBLIC_t type, + const TPMU_SENSITIVE_COMPOSITE_t *p) +{ + switch(type) + { + case TPM_ALG_RSA: + grub_Tss2_MU_TPM2B_Marshal (buffer, p->rsa.size, p->rsa.buffer); + break; + case TPM_ALG_ECC: + grub_Tss2_MU_TPM2B_Marshal (buffer, p->ecc.size, p->ecc.buffer); + break; + case TPM_ALG_KEYEDHASH: + grub_Tss2_MU_TPM2B_Marshal (buffer, p->bits.size, p->bits.buffer); + break; + case TPM_ALG_SYMCIPHER: + grub_Tss2_MU_TPM2B_Marshal (buffer, p->sym.size, p->sym.buffer); + break; + default: + buffer->error = 1; + } +} + +void +grub_Tss2_MU_TPMT_SENSITIVE_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_SENSITIVE_t *p) +{ + grub_tpm2_buffer_pack_u16 (buffer, p->sensitiveType); + grub_Tss2_MU_TPM2B_Marshal (buffer, p->authValue.size, p->authValue.buffer); + grub_Tss2_MU_TPM2B_Marshal (buffer, p->seedValue.size, p->seedValue.buffer); + grub_Tss2_MU_TPMU_SENSITIVE_COMPOSITE_Marshal (buffer, p->sensitiveType, &p->sensitive); +} + +void +grub_Tss2_MU_TPM2B_SENSITIVE_Marshal (grub_tpm2_buffer_t buffer, + const TPM2B_SENSITIVE_t *p) +{ + grub_tpm2_buffer_pack_u16 (buffer, p->size); + grub_Tss2_MU_TPMT_SENSITIVE_Marshal (buffer, &p->sensitiveArea); +} + +void +grub_Tss2_MU_TPM2B_SENSITIVE_CREATE_Marshal (grub_tpm2_buffer_t buffer, + const TPM2B_SENSITIVE_CREATE_t *sensitiveCreate) +{ + grub_uint32_t start; + grub_uint16_t size; + + if (sensitiveCreate) + { + grub_tpm2_buffer_pack_u16 (buffer, sensitiveCreate->size); + start = buffer->size; + grub_Tss2_MU_TPMS_SENSITIVE_CREATE_Marshal (buffer, &sensitiveCreate->sensitive); + size = grub_cpu_to_be16 (buffer->size - start); + + grub_memcpy (&buffer->data[start - sizeof (grub_uint16_t)], &size, sizeof (size)); + } + else + grub_tpm2_buffer_pack_u16 (buffer, 0); +} + +void +grub_Tss2_MU_TPMS_SIGNATURE_RSA_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_SIGNATURE_RSA_t *p) +{ + grub_tpm2_buffer_pack_u16 (buffer, p->hash); + grub_Tss2_MU_TPM2B_Marshal (buffer, p->sig.size, p->sig.buffer); +} + +void +grub_Tss2_MU_TPMS_SIGNATURE_ECC_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_SIGNATURE_ECC_t *p) +{ + grub_tpm2_buffer_pack_u16 (buffer, p->hash); + grub_Tss2_MU_TPM2B_Marshal (buffer, p->signatureR.size, p->signatureR.buffer); + grub_Tss2_MU_TPM2B_Marshal (buffer, p->signatureS.size, p->signatureS.buffer); +} + +void +grub_Tss2_MU_TPMU_HA_Marshal (grub_tpm2_buffer_t buffer, + const TPMI_ALG_HASH_t hashAlg, + const TPMU_HA_t *p) +{ + grub_uint16_t i; + + switch (hashAlg) + { + case TPM_ALG_SHA1: + for (i = 0; i < TPM_SHA1_DIGEST_SIZE; i++) + grub_tpm2_buffer_pack_u8 (buffer, p->sha1[i]); + break; + case TPM_ALG_SHA256: + for (i = 0; i < TPM_SHA256_DIGEST_SIZE; i++) + grub_tpm2_buffer_pack_u8 (buffer, p->sha256[i]); + break; + case TPM_ALG_SHA384: + for (i = 0; i < TPM_SHA384_DIGEST_SIZE; i++) + grub_tpm2_buffer_pack_u8 (buffer, p->sha384[i]); + break; + case TPM_ALG_SHA512: + for (i = 0; i < TPM_SHA512_DIGEST_SIZE; i++) + grub_tpm2_buffer_pack_u8 (buffer, p->sha512[i]); + break; + default: + buffer->error = 1; + break; + } +} + +void +grub_Tss2_MU_TPMT_HA_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_HA_t *p) +{ + grub_tpm2_buffer_pack_u16 (buffer, p->hashAlg); + grub_Tss2_MU_TPMU_HA_Marshal (buffer, p->hashAlg, &p->digest); +} + +void +grub_Tss2_MU_TPMU_SIGNATURE_Marshal (grub_tpm2_buffer_t buffer, + const TPMI_ALG_SIG_SCHEME_t sigAlg, + const TPMU_SIGNATURE_t *p) +{ + switch (sigAlg) + { + case TPM_ALG_RSASSA: + grub_Tss2_MU_TPMS_SIGNATURE_RSA_Marshal (buffer, (TPMS_SIGNATURE_RSA_t *) &p->rsassa); + break; + case TPM_ALG_RSAPSS: + grub_Tss2_MU_TPMS_SIGNATURE_RSA_Marshal (buffer, (TPMS_SIGNATURE_RSA_t *) &p->rsapss); + break; + case TPM_ALG_ECDSA: + grub_Tss2_MU_TPMS_SIGNATURE_ECC_Marshal (buffer, (TPMS_SIGNATURE_ECC_t *) &p->ecdsa); + break; + case TPM_ALG_ECDAA: + grub_Tss2_MU_TPMS_SIGNATURE_ECC_Marshal (buffer, (TPMS_SIGNATURE_ECC_t *) &p->ecdaa); + break; + case TPM_ALG_SM2: + grub_Tss2_MU_TPMS_SIGNATURE_ECC_Marshal (buffer, (TPMS_SIGNATURE_ECC_t *) &p->sm2); + break; + case TPM_ALG_ECSCHNORR: + grub_Tss2_MU_TPMS_SIGNATURE_ECC_Marshal (buffer, (TPMS_SIGNATURE_ECC_t *) &p->ecschnorr); + break; + case TPM_ALG_HMAC: + grub_Tss2_MU_TPMT_HA_Marshal (buffer, &p->hmac); + break; + case TPM_ALG_NULL: + break; + default: + buffer->error = 1; + break; + } +} + +void +grub_Tss2_MU_TPMT_SIGNATURE_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_SIGNATURE_t *p) +{ + grub_tpm2_buffer_pack_u16 (buffer, p->sigAlg); + grub_Tss2_MU_TPMU_SIGNATURE_Marshal (buffer, p->sigAlg, &p->signature); +} + +void +grub_Tss2_MU_TPMT_TK_VERIFIED_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_TK_VERIFIED_t *p) +{ + grub_tpm2_buffer_pack_u16 (buffer, p->tag); + grub_tpm2_buffer_pack_u32 (buffer, p->hierarchy); + grub_Tss2_MU_TPM2B_Marshal (buffer, p->digest.size, p->digest.buffer); +} + +static void +__Tss2_MU_TPM2B_BUFFER_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_t *p, grub_uint16_t bound) +{ + grub_tpm2_buffer_unpack_u16 (buffer, &p->size); + + if (p->size > bound) + { + buffer->error = 1; + return; + } + + grub_tpm2_buffer_unpack (buffer, &p->buffer, p->size); +} + +#define TPM2B_BUFFER_UNMARSHAL(buffer, type, data) \ + __Tss2_MU_TPM2B_BUFFER_Unmarshal(buffer, (TPM2B_t *)data, sizeof(type) - sizeof(grub_uint16_t)) + +void +grub_Tss2_MU_TPMS_AUTH_RESPONSE_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_AUTH_RESPONSE_t *p) +{ + grub_uint8_t tmp; + grub_uint32_t tmp32; + + grub_tpm2_buffer_unpack_u16 (buffer, &p->nonce.size); + + if (p->nonce.size) + grub_tpm2_buffer_unpack (buffer, &p->nonce.buffer, p->nonce.size); + + grub_tpm2_buffer_unpack_u8 (buffer, &tmp); + tmp32 = tmp; + grub_memcpy (&p->sessionAttributes, &tmp32, sizeof (grub_uint32_t)); + + grub_tpm2_buffer_unpack_u16 (buffer, &p->hmac.size); + + if (p->hmac.size) + grub_tpm2_buffer_unpack (buffer, &p->hmac.buffer, p->hmac.size); +} + +void +grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_DIGEST_t *digest) +{ + TPM2B_BUFFER_UNMARSHAL (buffer, TPM2B_DIGEST_t, digest); +} + +void +grub_Tss2_MU_TPM2B_NONCE_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_NONCE_t *nonce) +{ + TPM2B_BUFFER_UNMARSHAL (buffer, TPM2B_NONCE_t, nonce); +} + +void +grub_Tss2_MU_TPM2B_DATA_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_DATA_t *data) +{ + TPM2B_BUFFER_UNMARSHAL (buffer, TPM2B_DATA_t, data); +} + +void +grub_Tss2_MU_TPMS_CREATION_DATA_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_CREATION_DATA_t *data) +{ + grub_Tss2_MU_TPML_PCR_SELECTION_Unmarshal (buffer, &data->pcrSelect); + grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (buffer, &data->pcrDigest); + grub_tpm2_buffer_unpack_u8 (buffer, (grub_uint8_t *)&data->locality); + grub_tpm2_buffer_unpack_u16 (buffer, &data->parentNameAlg); + grub_Tss2_MU_TPM2B_NAME_Unmarshal (buffer, &data->parentName); + grub_Tss2_MU_TPM2B_NAME_Unmarshal (buffer, &data->parentQualifiedName); + grub_Tss2_MU_TPM2B_DATA_Unmarshal (buffer, &data->outsideInfo); +} + +void +grub_Tss2_MU_TPM2B_CREATION_DATA_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_CREATION_DATA_t *data) +{ + grub_tpm2_buffer_unpack_u16 (buffer, &data->size); + grub_Tss2_MU_TPMS_CREATION_DATA_Unmarshal (buffer, &data->creationData); +} + +void +grub_Tss2_MU_TPM2B_PRIVATE_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_PRIVATE_t *private) +{ + TPM2B_BUFFER_UNMARSHAL (buffer, TPM2B_PRIVATE_t, private); +} + +void +grub_Tss2_MU_TPM2B_SENSITIVE_DATA_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_SENSITIVE_DATA_t *data) +{ + TPM2B_BUFFER_UNMARSHAL (buffer, TPM2B_SENSITIVE_DATA_t, data); +} + +void +grub_Tss2_MU_TPM2B_PUBLIC_KEY_RSA_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_PUBLIC_KEY_RSA_t *rsa) +{ + TPM2B_BUFFER_UNMARSHAL (buffer, TPM2B_PUBLIC_KEY_RSA_t, rsa); +} + +void +grub_Tss2_MU_TPM2B_ECC_PARAMETER_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_ECC_PARAMETER_t *param) +{ + TPM2B_BUFFER_UNMARSHAL (buffer, TPM2B_ECC_PARAMETER_t, param); +} + +void +grub_Tss2_MU_TPMA_OBJECT_Unmarshal (grub_tpm2_buffer_t buffer, + TPMA_OBJECT_t *p) +{ + grub_tpm2_buffer_unpack_u32 (buffer, (grub_uint32_t *) p); +} + +void +grub_Tss2_MU_TPMS_SCHEME_HMAC_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_SCHEME_HMAC_t *p) +{ + grub_tpm2_buffer_unpack_u16 (buffer, &p->hashAlg); +} + +void +grub_Tss2_MU_TPMS_SCHEME_XOR_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_SCHEME_XOR_t *p) +{ + grub_tpm2_buffer_unpack_u16 (buffer, &p->hashAlg); + grub_tpm2_buffer_unpack_u16 (buffer, &p->kdf); +} + +void +grub_Tss2_MU_TPMU_SCHEME_KEYEDHASH_Unmarshal (grub_tpm2_buffer_t buffer, + TPMI_ALG_KEYEDHASH_SCHEME_t scheme, + TPMU_SCHEME_KEYEDHASH_t *p) +{ + switch (scheme) + { + case TPM_ALG_HMAC: + grub_Tss2_MU_TPMS_SCHEME_HMAC_Unmarshal (buffer, &p->hmac); + break; + case TPM_ALG_XOR: + grub_Tss2_MU_TPMS_SCHEME_XOR_Unmarshal (buffer, &p->exclusiveOr); + break; + case TPM_ALG_NULL: + break; + default: + buffer->error = 1; + break; + } +} + +void +grub_Tss2_MU_TPMT_KEYEDHASH_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_KEYEDHASH_SCHEME_t *p) +{ + grub_tpm2_buffer_unpack_u16 (buffer, &p->scheme); + grub_Tss2_MU_TPMU_SCHEME_KEYEDHASH_Unmarshal (buffer, p->scheme, &p->details); +} + +void +grub_Tss2_MU_TPMS_KEYEDHASH_PARMS_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_KEYEDHASH_PARMS_t *p) +{ + grub_Tss2_MU_TPMT_KEYEDHASH_SCHEME_Unmarshal (buffer, &p->scheme); +} + +void +grub_Tss2_MU_TPMU_SYM_KEY_BITS_Unmarshal (grub_tpm2_buffer_t buffer, + TPMI_ALG_SYM_OBJECT_t algorithm, + TPMU_SYM_KEY_BITS_t *p) +{ + switch (algorithm) + { + case TPM_ALG_AES: + case TPM_ALG_SM4: + case TPM_ALG_CAMELLIA: + case TPM_ALG_XOR: + grub_tpm2_buffer_unpack_u16 (buffer, (grub_uint16_t *) p); + break; + case TPM_ALG_NULL: + break; + default: + buffer->error = 1; + break; + } +} + +void +grub_Tss2_MU_TPMU_SYM_MODE_Unmarshal (grub_tpm2_buffer_t buffer, + TPMI_ALG_SYM_OBJECT_t algorithm, + TPMU_SYM_MODE_t *p) +{ + switch (algorithm) + { + case TPM_ALG_AES: + case TPM_ALG_SM4: + case TPM_ALG_CAMELLIA: + grub_tpm2_buffer_unpack_u16 (buffer, (grub_uint16_t *) p); + break; + case TPM_ALG_XOR: + case TPM_ALG_NULL: + break; + default: + buffer->error = 1; + break; + } +} + +void +grub_Tss2_MU_TPMT_SYM_DEF_OBJECT_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_SYM_DEF_OBJECT_t *p) +{ + grub_tpm2_buffer_unpack_u16 (buffer, &p->algorithm); + grub_Tss2_MU_TPMU_SYM_KEY_BITS_Unmarshal (buffer, p->algorithm, &p->keyBits); + grub_Tss2_MU_TPMU_SYM_MODE_Unmarshal (buffer, p->algorithm, &p->mode); +} + +void +grub_Tss2_MU_TPMS_SYMCIPHER_PARMS_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_SYMCIPHER_PARMS_t *p) +{ + grub_Tss2_MU_TPMT_SYM_DEF_OBJECT_Unmarshal (buffer, &p->sym); +} + +void +grub_Tss2_MU_TPMU_ASYM_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer, + TPMI_ALG_RSA_DECRYPT_t scheme, + TPMU_ASYM_SCHEME_t *p __attribute__((unused))) +{ + switch (scheme) + { + case TPM_ALG_NULL: + break; + default: + /* Unsupported */ + buffer->error = 1; + break; + } +} + +void +grub_Tss2_MU_TPMT_RSA_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_RSA_SCHEME_t *p) +{ + grub_tpm2_buffer_unpack_u16 (buffer, &p->scheme); + grub_Tss2_MU_TPMU_ASYM_SCHEME_Unmarshal (buffer, p->scheme, &p->details); +} + +void +grub_Tss2_MU_TPMS_RSA_PARMS_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_RSA_PARMS_t *p) +{ + grub_Tss2_MU_TPMT_SYM_DEF_OBJECT_Unmarshal (buffer, &p->symmetric); + grub_Tss2_MU_TPMT_RSA_SCHEME_Unmarshal (buffer, &p->scheme); + grub_tpm2_buffer_unpack_u16 (buffer, &p->keyBits); + grub_tpm2_buffer_unpack_u32 (buffer, &p->exponent); +} + +void +grub_Tss2_MU_TPMT_ECC_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_ECC_SCHEME_t *p) +{ + grub_tpm2_buffer_unpack_u16 (buffer, &p->scheme); + grub_Tss2_MU_TPMU_ASYM_SCHEME_Unmarshal (buffer, p->scheme, &p->details); +} + +void +grub_Tss2_MU_TPMU_KDF_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer, + TPMI_ALG_KDF_t scheme, + TPMU_KDF_SCHEME_t *p) +{ + switch (scheme) + { + case TPM_ALG_MGF1: + grub_tpm2_buffer_unpack_u16 (buffer, &p->mgf1.hashAlg); + break; + case TPM_ALG_KDF1_SP800_56A: + grub_tpm2_buffer_unpack_u16 (buffer, &p->kdf1_sp800_56a.hashAlg); + break; + case TPM_ALG_KDF2: + grub_tpm2_buffer_unpack_u16 (buffer, &p->kdf2.hashAlg); + break; + case TPM_ALG_KDF1_SP800_108: + grub_tpm2_buffer_unpack_u16 (buffer, &p->kdf1_sp800_108.hashAlg); + break; + case TPM_ALG_NULL: + break; + default: + buffer->error = 1; + break; + } +} + +void +grub_Tss2_MU_TPMT_KDF_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_KDF_SCHEME_t *p) +{ + grub_tpm2_buffer_unpack_u16 (buffer, &p->scheme); + grub_Tss2_MU_TPMU_KDF_SCHEME_Unmarshal (buffer, p->scheme, &p->details); +} + +void +grub_Tss2_MU_TPMS_ECC_PARMS_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_ECC_PARMS_t *p) +{ + grub_Tss2_MU_TPMT_SYM_DEF_OBJECT_Unmarshal (buffer, &p->symmetric); + grub_Tss2_MU_TPMT_ECC_SCHEME_Unmarshal (buffer, &p->scheme ); + grub_tpm2_buffer_unpack_u16 (buffer, &p->curveID); + grub_Tss2_MU_TPMT_KDF_SCHEME_Unmarshal (buffer, &p->kdf); +} + +void +grub_Tss2_MU_TPMU_PUBLIC_PARMS_Unmarshal (grub_tpm2_buffer_t buffer, + grub_uint32_t type, + TPMU_PUBLIC_PARMS_t *p) +{ + switch (type) + { + case TPM_ALG_KEYEDHASH: + grub_Tss2_MU_TPMS_KEYEDHASH_PARMS_Unmarshal (buffer, &p->keyedHashDetail); + break; + case TPM_ALG_SYMCIPHER: + grub_Tss2_MU_TPMS_SYMCIPHER_PARMS_Unmarshal (buffer, &p->symDetail); + break; + case TPM_ALG_RSA: + grub_Tss2_MU_TPMS_RSA_PARMS_Unmarshal (buffer, &p->rsaDetail); + break; + case TPM_ALG_ECC: + grub_Tss2_MU_TPMS_ECC_PARMS_Unmarshal (buffer, &p->eccDetail); + break; + default: + buffer->error = 1; + break; + } +} + +void +grub_Tss2_MU_TPMS_ECC_POINT_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_ECC_POINT_t *p) +{ + grub_Tss2_MU_TPM2B_ECC_PARAMETER_Unmarshal (buffer, &p->x); + grub_Tss2_MU_TPM2B_ECC_PARAMETER_Unmarshal (buffer, &p->y); +} + +void +grub_Tss2_MU_TPMU_PUBLIC_ID_Unmarshal (grub_tpm2_buffer_t buffer, + TPMI_ALG_PUBLIC_t type, + TPMU_PUBLIC_ID_t *p) +{ + switch(type) + { + case TPM_ALG_KEYEDHASH: + grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (buffer, &p->keyedHash); + break; + case TPM_ALG_SYMCIPHER: + grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (buffer, &p->sym); + break; + case TPM_ALG_RSA: + grub_Tss2_MU_TPM2B_PUBLIC_KEY_RSA_Unmarshal (buffer, &p->rsa); + break; + case TPM_ALG_ECC: + grub_Tss2_MU_TPMS_ECC_POINT_Unmarshal (buffer, &p->ecc); + break; + default: + buffer->error = 1; + break; + } +} + +void +grub_Tss2_MU_TPMT_PUBLIC_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_PUBLIC_t *p) +{ + grub_tpm2_buffer_unpack_u16 (buffer, &p->type); + grub_tpm2_buffer_unpack_u16 (buffer, &p->nameAlg); + grub_Tss2_MU_TPMA_OBJECT_Unmarshal (buffer, &p->objectAttributes); + grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (buffer, &p->authPolicy); + grub_Tss2_MU_TPMU_PUBLIC_PARMS_Unmarshal (buffer, p->type, &p->parameters); + grub_Tss2_MU_TPMU_PUBLIC_ID_Unmarshal (buffer, p->type, &p->unique); +} + +void +grub_Tss2_MU_TPM2B_PUBLIC_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_PUBLIC_t *p) +{ + grub_tpm2_buffer_unpack_u16 (buffer, &p->size); + grub_Tss2_MU_TPMT_PUBLIC_Unmarshal (buffer, &p->publicArea); +} + +void +grub_Tss2_MU_TPMS_NV_PUBLIC_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_NV_PUBLIC_t *p) +{ + grub_tpm2_buffer_unpack_u32 (buffer, &p->nvIndex); + grub_tpm2_buffer_unpack_u16 (buffer, &p->nameAlg); + grub_tpm2_buffer_unpack_u32 (buffer, &p->attributes); + grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (buffer, &p->authPolicy); + grub_tpm2_buffer_unpack_u16 (buffer, &p->dataSize); +} + +void +grub_Tss2_MU_TPM2B_NV_PUBLIC_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_NV_PUBLIC_t *p) +{ + grub_tpm2_buffer_unpack_u16 (buffer, &p->size); + grub_Tss2_MU_TPMS_NV_PUBLIC_Unmarshal (buffer, &p->nvPublic); +} + +void +grub_Tss2_MU_TPM2B_NAME_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_NAME_t *n) +{ + TPM2B_BUFFER_UNMARSHAL (buffer, TPM2B_NAME_t, n); +} + +void +grub_Tss2_MU_TPMS_TAGGED_PROPERTY_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_TAGGED_PROPERTY_t *property) +{ + grub_tpm2_buffer_unpack_u32 (buffer, &property->property); + grub_tpm2_buffer_unpack_u32 (buffer, &property->value); +} + +void +grub_Tss2_MU_TPMT_TK_CREATION_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_TK_CREATION_t *p) +{ + grub_tpm2_buffer_unpack_u16 (buffer, &p->tag); + grub_tpm2_buffer_unpack_u32 (buffer, &p->hierarchy); + grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (buffer, &p->digest); +} + +void +grub_Tss2_MU_TPMT_TK_HASHCHECK_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_TK_HASHCHECK_t *p) +{ + grub_tpm2_buffer_unpack_u16 (buffer, &p->tag); + grub_tpm2_buffer_unpack_u32 (buffer, &p->hierarchy); + grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (buffer, &p->digest); +} + +void +grub_Tss2_MU_TPMT_TK_VERIFIED_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_TK_VERIFIED_t *p) +{ + grub_tpm2_buffer_unpack_u16 (buffer, &p->tag); + grub_tpm2_buffer_unpack_u32 (buffer, &p->hierarchy); + grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (buffer, &p->digest); +} + +void +grub_Tss2_MU_TPMS_PCR_SELECTION_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_PCR_SELECTION_t *pcrSelection) +{ + grub_uint32_t i; + + grub_tpm2_buffer_unpack_u16 (buffer, &pcrSelection->hash); + grub_tpm2_buffer_unpack_u8 (buffer, &pcrSelection->sizeOfSelect); + + if (pcrSelection->sizeOfSelect > TPM_PCR_SELECT_MAX) + { + buffer->error = 1; + return; + } + + for (i = 0; i < pcrSelection->sizeOfSelect; i++) + grub_tpm2_buffer_unpack_u8 (buffer, &pcrSelection->pcrSelect[i]); +} + +void +grub_Tss2_MU_TPML_PCR_SELECTION_Unmarshal (grub_tpm2_buffer_t buffer, + TPML_PCR_SELECTION_t *pcrSelection) +{ + grub_uint32_t i; + + grub_tpm2_buffer_unpack_u32 (buffer, &pcrSelection->count); + + if (pcrSelection->count > TPM_NUM_PCR_BANKS) + { + buffer->error = 1; + return; + } + + for (i = 0; i < pcrSelection->count; i++) + grub_Tss2_MU_TPMS_PCR_SELECTION_Unmarshal (buffer, &pcrSelection->pcrSelections[i]); +} + +void +grub_Tss2_MU_TPML_DIGEST_Unmarshal (grub_tpm2_buffer_t buffer, + TPML_DIGEST_t *digest) +{ + grub_uint32_t i; + + grub_tpm2_buffer_unpack_u32 (buffer, &digest->count); + + if (digest->count > 8) + { + buffer->error = 1; + return; + } + + for (i = 0; i < digest->count; i++) + grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (buffer, &digest->digests[i]); +} + +void +grub_Tss2_MU_TPMS_SIGNATURE_RSA_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_SIGNATURE_RSA_t *rsa) +{ + grub_tpm2_buffer_unpack_u16 (buffer, &rsa->hash); + grub_Tss2_MU_TPM2B_PUBLIC_KEY_RSA_Unmarshal (buffer, &rsa->sig); +} + +void +grub_Tss2_MU_TPMS_SIGNATURE_ECC_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_SIGNATURE_ECC_t *ecc) +{ + grub_tpm2_buffer_unpack_u16 (buffer, &ecc->hash); + grub_Tss2_MU_TPM2B_ECC_PARAMETER_Unmarshal (buffer, &ecc->signatureR); + grub_Tss2_MU_TPM2B_ECC_PARAMETER_Unmarshal (buffer, &ecc->signatureS); +} + +void +grub_Tss2_MU_TPMU_HA_Unmarshal (grub_tpm2_buffer_t buffer, + TPMI_ALG_HASH_t hashAlg, + TPMU_HA_t *p) +{ + switch (hashAlg) + { + case TPM_ALG_SHA1: + grub_tpm2_buffer_unpack (buffer, &p->sha1, TPM_SHA1_DIGEST_SIZE); + break; + case TPM_ALG_SHA256: + grub_tpm2_buffer_unpack (buffer, &p->sha256, TPM_SHA256_DIGEST_SIZE); + break; + case TPM_ALG_SHA384: + grub_tpm2_buffer_unpack (buffer, &p->sha384, TPM_SHA384_DIGEST_SIZE); + break; + case TPM_ALG_SHA512: + grub_tpm2_buffer_unpack (buffer, &p->sha512, TPM_SHA512_DIGEST_SIZE); + break; + default: + buffer->error = 1; + break; + } +} + +void +grub_Tss2_MU_TPMT_HA_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_HA_t *p) +{ + grub_tpm2_buffer_unpack_u16 (buffer, &p->hashAlg); + grub_Tss2_MU_TPMU_HA_Unmarshal (buffer, p->hashAlg, &p->digest); +} + +void +grub_Tss2_MU_TPMU_SIGNATURE_Unmarshal (grub_tpm2_buffer_t buffer, + TPMI_ALG_SIG_SCHEME_t sigAlg, + TPMU_SIGNATURE_t *p) +{ + switch (sigAlg) + { + case TPM_ALG_RSASSA: + grub_Tss2_MU_TPMS_SIGNATURE_RSA_Unmarshal (buffer, (TPMS_SIGNATURE_RSA_t *)&p->rsassa); + break; + case TPM_ALG_RSAPSS: + grub_Tss2_MU_TPMS_SIGNATURE_RSA_Unmarshal (buffer, (TPMS_SIGNATURE_RSA_t *)&p->rsapss); + break; + case TPM_ALG_ECDSA: + grub_Tss2_MU_TPMS_SIGNATURE_ECC_Unmarshal (buffer, (TPMS_SIGNATURE_ECC_t *)&p->ecdsa); + break; + case TPM_ALG_ECDAA: + grub_Tss2_MU_TPMS_SIGNATURE_ECC_Unmarshal (buffer, (TPMS_SIGNATURE_ECC_t *)&p->ecdaa); + break; + case TPM_ALG_SM2: + grub_Tss2_MU_TPMS_SIGNATURE_ECC_Unmarshal (buffer, (TPMS_SIGNATURE_ECC_t *)&p->sm2); + break; + case TPM_ALG_ECSCHNORR: + grub_Tss2_MU_TPMS_SIGNATURE_ECC_Unmarshal (buffer, (TPMS_SIGNATURE_ECC_t *)&p->ecschnorr); + break; + case TPM_ALG_HMAC: + grub_Tss2_MU_TPMT_HA_Unmarshal (buffer, &p->hmac); + break; + case TPM_ALG_NULL: + break; + default: + buffer->error = 1; + break; + } +} + +void +grub_Tss2_MU_TPMT_SIGNATURE_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_SIGNATURE_t *p) +{ + grub_tpm2_buffer_unpack_u16 (buffer, &p->sigAlg); + grub_Tss2_MU_TPMU_SIGNATURE_Unmarshal (buffer, p->sigAlg, &p->signature); +} diff --git a/grub-core/lib/tss2/tss2_mu.h b/grub-core/lib/tss2/tss2_mu.h new file mode 100644 index 000000000..8f82126e1 --- /dev/null +++ b/grub-core/lib/tss2/tss2_mu.h @@ -0,0 +1,397 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 GRUB_TPM2_MU_HEADER +#define GRUB_TPM2_MU_HEADER 1 + +#include +#include + +extern void +grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_AUTH_COMMAND_t *authCommand); + +extern void +grub_Tss2_MU_TPM2B_Marshal (grub_tpm2_buffer_t buffer, + const grub_uint16_t size, + const grub_uint8_t *b); + +extern void +grub_Tss2_MU_TPMU_SYM_KEY_BITS_Marshal (grub_tpm2_buffer_t buffer, + const TPMI_ALG_SYM_OBJECT_t algorithm, + const TPMU_SYM_KEY_BITS_t *p); + +extern void +grub_Tss2_MU_TPMU_SYM_MODE_Marshal (grub_tpm2_buffer_t buffer, + const TPMI_ALG_SYM_OBJECT_t algorithm, + const TPMU_SYM_MODE_t *p); + +extern void +grub_Tss2_MU_TPMT_SYM_DEF_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_SYM_DEF_t *p); + +extern void +grub_Tss2_MU_TPMS_PCR_SELECTION_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_PCR_SELECTION_t *pcrSelection); + +extern void +grub_Tss2_MU_TPML_PCR_SELECTION_Marshal (grub_tpm2_buffer_t buffer, + const TPML_PCR_SELECTION_t *pcrSelection); + +extern void +grub_Tss2_MU_TPMA_OBJECT_Marshal (grub_tpm2_buffer_t buffer, + const TPMA_OBJECT_t *p); + +extern void +grub_Tss2_MU_TPMS_SCHEME_XOR_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_SCHEME_XOR_t *p); + +extern void +grub_Tss2_MU_TPMS_SCHEME_HMAC_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_SCHEME_HMAC_t *p); + +extern void +grub_Tss2_MU_TPMU_SCHEME_KEYEDHASH_Marshal (grub_tpm2_buffer_t buffer, + const TPMI_ALG_KEYEDHASH_SCHEME_t scheme, + const TPMU_SCHEME_KEYEDHASH_t *p); + +extern void +grub_Tss2_MU_TPMT_KEYEDHASH_SCHEME_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_KEYEDHASH_SCHEME_t *p); + +extern void +grub_Tss2_MU_TPMS_KEYEDHASH_PARMS_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_KEYEDHASH_PARMS_t *p); + +extern void +grub_Tss2_MU_TPMT_SYM_DEF_OBJECT_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_SYM_DEF_OBJECT_t *p); + +extern void +grub_Tss2_MU_TPMU_ASYM_SCHEME_Marshal (grub_tpm2_buffer_t buffer, + const TPMI_ALG_RSA_DECRYPT_t scheme, + const TPMU_ASYM_SCHEME_t *p); + +extern void +grub_Tss2_MU_TPMT_RSA_SCHEME_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_RSA_SCHEME_t *p); + +extern void +grub_Tss2_MU_TPMS_RSA_PARMS_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_RSA_PARMS_t *p); + +extern void +grub_Tss2_MU_TPMS_SYMCIPHER_PARMS_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_SYMCIPHER_PARMS_t *p); + +extern void +grub_Tss2_MU_TPMT_ECC_SCHEME_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_ECC_SCHEME_t *p); + +extern void +grub_Tss2_MU_TPMU_KDF_SCHEME_Marshal (grub_tpm2_buffer_t buffer, + const TPMI_ALG_KDF_t scheme, + const TPMU_KDF_SCHEME_t *p); + +extern void +grub_Tss2_MU_TPMT_KDF_SCHEME_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_KDF_SCHEME_t *p); + +extern void +grub_Tss2_MU_TPMS_ECC_PARMS_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_ECC_PARMS_t *p); + +extern void +grub_Tss2_MU_TPMU_PUBLIC_PARMS_Marshal (grub_tpm2_buffer_t buffer, + const grub_uint32_t type, + const TPMU_PUBLIC_PARMS_t *p); + +extern void +grub_Tss2_MU_TPMS_ECC_POINT_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_ECC_POINT_t *p); + +extern void +grub_Tss2_MU_TPMU_PUBLIC_ID_Marshal (grub_tpm2_buffer_t buffer, + const TPMI_ALG_PUBLIC_t type, + const TPMU_PUBLIC_ID_t *p); + +extern void +grub_Tss2_MU_TPMT_PUBLIC_PARMS_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_PUBLIC_PARMS_t *p); + +extern void +grub_Tss2_MU_TPMT_PUBLIC_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_PUBLIC_t *p); + +extern void +grub_Tss2_MU_TPM2B_PUBLIC_Marshal (grub_tpm2_buffer_t buffer, + const TPM2B_PUBLIC_t *p); + +extern void +grub_Tss2_MU_TPMS_SENSITIVE_CREATE_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_SENSITIVE_CREATE_t *p); + +extern void +grub_Tss2_MU_TPM2B_SENSITIVE_CREATE_Marshal (grub_tpm2_buffer_t buffer, + const TPM2B_SENSITIVE_CREATE_t *sensitiveCreate); + +extern void +grub_Tss2_MU_TPMU_SENSITIVE_COMPOSITE_Marshal (grub_tpm2_buffer_t buffer, + const TPMI_ALG_PUBLIC_t type, + const TPMU_SENSITIVE_COMPOSITE_t *p); +extern void +grub_Tss2_MU_TPMT_SENSITIVE_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_SENSITIVE_t *p); + +extern void +grub_Tss2_MU_TPM2B_SENSITIVE_Marshal (grub_tpm2_buffer_t buffer, + const TPM2B_SENSITIVE_t *p); + +extern void +grub_Tss2_MU_TPMS_SIGNATURE_RSA_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_SIGNATURE_RSA_t *p); + +extern void +grub_Tss2_MU_TPMS_SIGNATURE_ECC_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_SIGNATURE_ECC_t *p); + +extern void +grub_Tss2_MU_TPMU_HA_Marshal (grub_tpm2_buffer_t buffer, + const TPMI_ALG_HASH_t hashAlg, + const TPMU_HA_t *p); + +extern void +grub_Tss2_MU_TPMT_HA_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_HA_t *p); + +extern void +grub_Tss2_MU_TPMU_SIGNATURE_Marshal (grub_tpm2_buffer_t buffer, + const TPMI_ALG_SIG_SCHEME_t sigAlg, + const TPMU_SIGNATURE_t *p); + +extern void +grub_Tss2_MU_TPMT_SIGNATURE_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_SIGNATURE_t *p); + +extern void +grub_Tss2_MU_TPMT_TK_VERIFIED_Marshal (grub_tpm2_buffer_t buffer, + const TPMT_TK_VERIFIED_t *p); + +extern void +grub_Tss2_MU_TPMS_AUTH_RESPONSE_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_AUTH_RESPONSE_t *p); + +extern void +grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_DIGEST_t *digest); + +extern void +grub_Tss2_MU_TPM2B_NONCE_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_NONCE_t *nonce); + +extern void +grub_Tss2_MU_TPM2B_DATA_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_DATA_t *data); + +extern void +grub_Tss2_MU_TPMS_CREATION_DATA_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_CREATION_DATA_t *data); + +extern void +grub_Tss2_MU_TPM2B_CREATION_DATA_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_CREATION_DATA_t *data); + +extern void +grub_Tss2_MU_TPM2B_PRIVATE_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_PRIVATE_t *private); + +extern void +grub_Tss2_MU_TPM2B_SENSITIVE_DATA_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_SENSITIVE_DATA_t *data); + +extern void +grub_Tss2_MU_TPM2B_PUBLIC_KEY_RSA_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_PUBLIC_KEY_RSA_t *rsa); + +extern void +grub_Tss2_MU_TPM2B_ECC_PARAMETER_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_ECC_PARAMETER_t *param); + +extern void +grub_Tss2_MU_TPMA_OBJECT_Unmarshal (grub_tpm2_buffer_t buffer, + TPMA_OBJECT_t *p); + +extern void +grub_Tss2_MU_TPMS_SCHEME_HMAC_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_SCHEME_HMAC_t *p); + +extern void +grub_Tss2_MU_TPMS_SCHEME_XOR_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_SCHEME_XOR_t *p); + +extern void +grub_Tss2_MU_TPMU_SCHEME_KEYEDHASH_Unmarshal (grub_tpm2_buffer_t buffer, + TPMI_ALG_KEYEDHASH_SCHEME_t scheme, + TPMU_SCHEME_KEYEDHASH_t *p); + +extern void +grub_Tss2_MU_TPMT_KEYEDHASH_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_KEYEDHASH_SCHEME_t *p); + +extern void +grub_Tss2_MU_TPMS_KEYEDHASH_PARMS_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_KEYEDHASH_PARMS_t *p); + +extern void +grub_Tss2_MU_TPMU_SYM_KEY_BITS_Unmarshal (grub_tpm2_buffer_t buffer, + TPMI_ALG_SYM_OBJECT_t algorithm, + TPMU_SYM_KEY_BITS_t *p); + +extern void +grub_Tss2_MU_TPMU_SYM_MODE_Unmarshal (grub_tpm2_buffer_t buffer, + TPMI_ALG_SYM_OBJECT_t algorithm, + TPMU_SYM_MODE_t *p); + +extern void +grub_Tss2_MU_TPMT_SYM_DEF_OBJECT_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_SYM_DEF_OBJECT_t *p); + +extern void +grub_Tss2_MU_TPMS_SYMCIPHER_PARMS_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_SYMCIPHER_PARMS_t *p); + +extern void +grub_Tss2_MU_TPMU_ASYM_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer, + TPMI_ALG_RSA_DECRYPT_t scheme, + TPMU_ASYM_SCHEME_t *p); + +extern void +grub_Tss2_MU_TPMT_RSA_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_RSA_SCHEME_t *p); + +extern void +grub_Tss2_MU_TPMS_RSA_PARMS_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_RSA_PARMS_t *p); + +extern void +grub_Tss2_MU_TPMT_ECC_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_ECC_SCHEME_t *p); + +extern void +grub_Tss2_MU_TPMU_KDF_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer, + TPMI_ALG_KDF_t scheme, + TPMU_KDF_SCHEME_t *p); + +extern void +grub_Tss2_MU_TPMT_KDF_SCHEME_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_KDF_SCHEME_t *p); + +extern void +grub_Tss2_MU_TPMS_ECC_PARMS_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_ECC_PARMS_t *p); + +extern void +grub_Tss2_MU_TPMU_PUBLIC_PARMS_Unmarshal (grub_tpm2_buffer_t buffer, + grub_uint32_t type, + TPMU_PUBLIC_PARMS_t *p); + +extern void +grub_Tss2_MU_TPMS_ECC_POINT_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_ECC_POINT_t *p); + +extern void +grub_Tss2_MU_TPMU_PUBLIC_ID_Unmarshal (grub_tpm2_buffer_t buffer, + TPMI_ALG_PUBLIC_t type, + TPMU_PUBLIC_ID_t *p); + +extern void +grub_Tss2_MU_TPMT_PUBLIC_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_PUBLIC_t *p); + +extern void +grub_Tss2_MU_TPM2B_PUBLIC_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_PUBLIC_t *p); + +extern void +grub_Tss2_MU_TPMS_NV_PUBLIC_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_NV_PUBLIC_t *p); + +extern void +grub_Tss2_MU_TPM2B_NV_PUBLIC_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_NV_PUBLIC_t *p); + +extern void +grub_Tss2_MU_TPM2B_NAME_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_NAME_t *n); + +extern void +grub_Tss2_MU_TPMS_TAGGED_PROPERTY_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_TAGGED_PROPERTY_t *property); + +extern void +grub_Tss2_MU_TPMT_TK_CREATION_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_TK_CREATION_t *p); + +extern void +grub_Tss2_MU_TPMT_TK_HASHCHECK_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_TK_HASHCHECK_t *p); + +extern void +grub_Tss2_MU_TPMT_TK_VERIFIED_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_TK_VERIFIED_t *p); + +extern void +grub_Tss2_MU_TPMS_PCR_SELECTION_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_PCR_SELECTION_t *pcrSelection); + +extern void +grub_Tss2_MU_TPML_PCR_SELECTION_Unmarshal (grub_tpm2_buffer_t buffer, + TPML_PCR_SELECTION_t *pcrSelection); + +extern void +grub_Tss2_MU_TPML_DIGEST_Unmarshal (grub_tpm2_buffer_t buffer, + TPML_DIGEST_t *digest); + +extern void +grub_Tss2_MU_TPMS_SIGNATURE_RSA_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_SIGNATURE_RSA_t *p); + +extern void +grub_Tss2_MU_TPMS_SIGNATURE_ECC_Unmarshal (grub_tpm2_buffer_t buffer, + TPMS_SIGNATURE_ECC_t *p); + +extern void +grub_Tss2_MU_TPMU_HA_Unmarshal (grub_tpm2_buffer_t buffer, + TPMI_ALG_HASH_t hashAlg, + TPMU_HA_t *p); + +extern void +grub_Tss2_MU_TPMT_HA_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_HA_t *p); + +extern void +grub_Tss2_MU_TPMU_SIGNATURE_Unmarshal (grub_tpm2_buffer_t buffer, + TPMI_ALG_SIG_SCHEME_t sigAlg, + TPMU_SIGNATURE_t *p); + +extern void +grub_Tss2_MU_TPMT_SIGNATURE_Unmarshal (grub_tpm2_buffer_t buffer, + TPMT_SIGNATURE_t *p); + +#endif /* ! GRUB_TPM2_MU_HEADER */ diff --git a/grub-core/lib/tss2/tss2_structs.h b/grub-core/lib/tss2/tss2_structs.h new file mode 100644 index 000000000..ca33db3ec --- /dev/null +++ b/grub-core/lib/tss2/tss2_structs.h @@ -0,0 +1,796 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 GRUB_TPM2_INTERNAL_STRUCTS_HEADER +#define GRUB_TPM2_INTERNAL_STRUCTS_HEADER 1 + +#include + +/* + * TPM response header + * This struct is used to calculate the minimum size of the TPM 2.0 response. + * The format of the response: + * + * +----------------------+ + * | UINT16 tag | + * +----------------------+ + * | UINT32 repsonse_size | + * +----------------------+ + * | UINT32 response_code | + * +======================+ + * | response_data | (optional) + * +======================+ + */ +struct __attribute__ ((__packed__)) TPM_RESPONSE_HEADER +{ + grub_uint16_t tag; + grub_uint32_t response_size; + TPM_RC_t response_code; +}; +typedef struct TPM_RESPONSE_HEADER TPM_RESPONSE_HEADER_t; + +/* TPMS_TAGGED_PROPERTY Structure */ +struct TPMS_TAGGED_PROPERTY +{ + TPM_PT_t property; + grub_uint32_t value; +}; +typedef struct TPMS_TAGGED_PROPERTY TPMS_TAGGED_PROPERTY_t; + +/* TPML_TAGGED_TPM_PROPERTY Structure */ +struct TPML_TAGGED_TPM_PROPERTY +{ + grub_uint32_t count; + TPMS_TAGGED_PROPERTY_t tpmProperty[TPM_MAX_TPM_PROPERTIES]; +}; +typedef struct TPML_TAGGED_TPM_PROPERTY TPML_TAGGED_TPM_PROPERTY_t; + +/* TPMU_CAPABILITIES Structure */ +union TPMU_CAPABILITIES +{ + TPML_TAGGED_TPM_PROPERTY_t tpmProperties; +}; +typedef union TPMU_CAPABILITIES TPMU_CAPABILITIES_t; + +/* TPMS_CAPABILITY_DATA Structure */ +struct TPMS_CAPABILITY_DATA +{ + TPM_CAP_t capability; + TPMU_CAPABILITIES_t data; +}; +typedef struct TPMS_CAPABILITY_DATA TPMS_CAPABILITY_DATA_t; + +/* TPMS_PCR_SELECT Structure */ +struct TPMS_PCR_SELECT +{ + grub_uint8_t sizeOfSelect; + grub_uint8_t pcrSelect[TPM_PCR_SELECT_MAX]; +}; +typedef struct TPMS_PCR_SELECT TPMS_PCR_SELECT_t; + +/* TPMS_PCR_SELECTION Structure */ +struct TPMS_PCR_SELECTION +{ + TPMI_ALG_HASH_t hash; + grub_uint8_t sizeOfSelect; + grub_uint8_t pcrSelect[TPM_PCR_SELECT_MAX]; +}; +typedef struct TPMS_PCR_SELECTION TPMS_PCR_SELECTION_t; + +static inline void TPMS_PCR_SELECTION_SelectPCR(TPMS_PCR_SELECTION_t *self, grub_uint32_t n) +{ + self->pcrSelect[(n / 8)] |= (1 << (n % 8)); +} + +/* TPML_PCR_SELECTION Structure */ +struct TPML_PCR_SELECTION +{ + grub_uint32_t count; + TPMS_PCR_SELECTION_t pcrSelections[TPM_NUM_PCR_BANKS]; +}; +typedef struct TPML_PCR_SELECTION TPML_PCR_SELECTION_t; + +/* TPMU_HA Structure */ +union TPMU_HA +{ + grub_uint8_t sha1[TPM_SHA1_DIGEST_SIZE]; + grub_uint8_t sha256[TPM_SHA256_DIGEST_SIZE]; + grub_uint8_t sha384[TPM_SHA384_DIGEST_SIZE]; + grub_uint8_t sha512[TPM_SHA512_DIGEST_SIZE]; + grub_uint8_t sm3_256[TPM_SM3_256_DIGEST_SIZE]; +}; +typedef union TPMU_HA TPMU_HA_t; + +/* TPM2B Structure */ +struct TPM2B +{ + grub_uint16_t size; + grub_uint8_t buffer[1]; +}; +typedef struct TPM2B TPM2B_t; + +/* TPM2B_DIGEST Structure */ +struct TPM2B_DIGEST +{ + grub_uint16_t size; + grub_uint8_t buffer[sizeof(TPMU_HA_t)]; +}; +typedef struct TPM2B_DIGEST TPM2B_DIGEST_t; + +/* TPML_DIGEST Structure */ +struct TPML_DIGEST +{ + grub_uint32_t count; + TPM2B_DIGEST_t digests[8]; +}; +typedef struct TPML_DIGEST TPML_DIGEST_t; + +/* TPM2B_NONCE Type */ +typedef TPM2B_DIGEST_t TPM2B_NONCE_t; + +/* TPMA_SESSION Structure */ +struct TPMA_SESSION +{ + grub_uint8_t continueSession:1; + grub_uint8_t auditExclusive:1; + grub_uint8_t auditReset:1; + grub_uint8_t reserved:2; + grub_uint8_t decrypt:1; + grub_uint8_t encrypt:1; + grub_uint8_t audit:1; +}; +typedef struct TPMA_SESSION TPMA_SESSION_t; + +/* TPM2B_AUTH Type */ +typedef TPM2B_DIGEST_t TPM2B_AUTH_t; + +/* TPMS_AUTH_COMMAND Structure */ +struct TPMS_AUTH_COMMAND +{ + TPMI_SH_AUTH_SESSION_t sessionHandle; + TPM2B_NONCE_t nonce; + TPMA_SESSION_t sessionAttributes; + TPM2B_AUTH_t hmac; +}; +typedef struct TPMS_AUTH_COMMAND TPMS_AUTH_COMMAND_t; + +/* TPMS_AUTH_RESPONSE Structure */ +struct TPMS_AUTH_RESPONSE +{ + TPM2B_NONCE_t nonce; + TPMA_SESSION_t sessionAttributes; + TPM2B_AUTH_t hmac; +}; +typedef struct TPMS_AUTH_RESPONSE TPMS_AUTH_RESPONSE_t; + +/* TPM2B_SENSITIVE_DATA Structure */ +struct TPM2B_SENSITIVE_DATA +{ + grub_uint16_t size; + grub_uint8_t buffer[TPM_MAX_SYM_DATA]; +}; +typedef struct TPM2B_SENSITIVE_DATA TPM2B_SENSITIVE_DATA_t; + +/* TPMS_SENSITIVE_CREATE Structure */ +struct TPMS_SENSITIVE_CREATE +{ + TPM2B_AUTH_t userAuth; + TPM2B_SENSITIVE_DATA_t data; +}; +typedef struct TPMS_SENSITIVE_CREATE TPMS_SENSITIVE_CREATE_t; + +/* TPM2B_SENSITIVE_CREATE Structure */ +struct TPM2B_SENSITIVE_CREATE +{ + grub_uint16_t size; + TPMS_SENSITIVE_CREATE_t sensitive; +}; +typedef struct TPM2B_SENSITIVE_CREATE TPM2B_SENSITIVE_CREATE_t; + +/* TPMA_OBJECT Structure */ +struct TPMA_OBJECT +{ + grub_uint32_t reserved1:1; + grub_uint32_t fixedTPM:1; + grub_uint32_t stClear:1; + grub_uint32_t reserved2:1; + grub_uint32_t fixedParent:1; + grub_uint32_t sensitiveDataOrigin:1; + grub_uint32_t userWithAuth:1; + grub_uint32_t adminWithPolicy:1; + grub_uint32_t reserved3:2; + grub_uint32_t noDA:1; + grub_uint32_t encryptedDuplication:1; + grub_uint32_t reserved4:4; + grub_uint32_t restricted:1; + grub_uint32_t decrypt:1; + grub_uint32_t sign:1; + grub_uint32_t reserved5:13; +}; +typedef struct TPMA_OBJECT TPMA_OBJECT_t; + +/* TPMS_SCHEME_HASH Structure */ +struct TPMS_SCHEME_HASH +{ + TPMI_ALG_HASH_t hashAlg; +}; +typedef struct TPMS_SCHEME_HASH TPMS_SCHEME_HASH_t; + +/* TPMS_SCHEME_HASH Types */ +typedef TPMS_SCHEME_HASH_t TPMS_KEY_SCHEME_ECDH_t; +typedef TPMS_SCHEME_HASH_t TPMS_KEY_SCHEME_ECMQV_t; +typedef TPMS_SCHEME_HASH_t TPMS_SIG_SCHEME_RSASSA_t; +typedef TPMS_SCHEME_HASH_t TPMS_SIG_SCHEME_RSAPSS_t; +typedef TPMS_SCHEME_HASH_t TPMS_SIG_SCHEME_ECDSA_t; +typedef TPMS_SCHEME_HASH_t TPMS_SIG_SCHEME_ECDAA_t; +typedef TPMS_SCHEME_HASH_t TPMS_SIG_SCHEME_SM2_t; +typedef TPMS_SCHEME_HASH_t TPMS_SIG_SCHEME_ECSCHNORR_t; +typedef TPMS_SCHEME_HASH_t TPMS_ENC_SCHEME_RSAES_t; +typedef TPMS_SCHEME_HASH_t TPMS_ENC_SCHEME_OAEP_t; +typedef TPMS_SCHEME_HASH_t TPMS_SCHEME_KDF2_t; +typedef TPMS_SCHEME_HASH_t TPMS_SCHEME_MGF1_t; +typedef TPMS_SCHEME_HASH_t TPMS_SCHEME_KDF1_SP800_56A_t; +typedef TPMS_SCHEME_HASH_t TPMS_SCHEME_KDF2_t; +typedef TPMS_SCHEME_HASH_t TPMS_SCHEME_KDF1_SP800_108_t; + +/* TPMS_SCHEME_HMAC Type */ +typedef TPMS_SCHEME_HASH_t TPMS_SCHEME_HMAC_t; + +/* TPMS_SCHEME_XOR Structure */ +struct TPMS_SCHEME_XOR +{ + TPMI_ALG_HASH_t hashAlg; + TPMI_ALG_KDF_t kdf; +}; +typedef struct TPMS_SCHEME_XOR TPMS_SCHEME_XOR_t; + +/* TPMU_SCHEME_KEYEDHASH Union */ +union TPMU_SCHEME_KEYEDHASH +{ + TPMS_SCHEME_HMAC_t hmac; + TPMS_SCHEME_XOR_t exclusiveOr; +}; +typedef union TPMU_SCHEME_KEYEDHASH TPMU_SCHEME_KEYEDHASH_t; + +/* TPMT_KEYEDHASH_SCHEME Structure */ +struct TPMT_KEYEDHASH_SCHEME +{ + TPMI_ALG_KEYEDHASH_SCHEME_t scheme; + TPMU_SCHEME_KEYEDHASH_t details; +}; +typedef struct TPMT_KEYEDHASH_SCHEME TPMT_KEYEDHASH_SCHEME_t; + +/* TPMS_KEYEDHASH_PARMS Structure */ +struct TPMS_KEYEDHASH_PARMS +{ + TPMT_KEYEDHASH_SCHEME_t scheme; +}; +typedef struct TPMS_KEYEDHASH_PARMS TPMS_KEYEDHASH_PARMS_t; + +/* TPMU_SYM_KEY_BITS Union */ +union TPMU_SYM_KEY_BITS +{ + TPM_KEY_BITS_t aes; + TPM_KEY_BITS_t exclusiveOr; + TPM_KEY_BITS_t sm4; + TPM_KEY_BITS_t camellia; +}; +typedef union TPMU_SYM_KEY_BITS TPMU_SYM_KEY_BITS_t; + +/* TPMU_SYM_MODE Union */ +union TPMU_SYM_MODE +{ + TPMI_ALG_SYM_MODE_t aes; + TPMI_ALG_SYM_MODE_t sm4; + TPMI_ALG_SYM_MODE_t camellia; + TPMI_ALG_SYM_MODE_t sym; +}; +typedef union TPMU_SYM_MODE TPMU_SYM_MODE_t; + +/* TPMT_SYM_DEF_OBJECT Structure */ +struct TPMT_SYM_DEF_OBJECT +{ + TPMI_ALG_SYM_OBJECT_t algorithm; + TPMU_SYM_KEY_BITS_t keyBits; + TPMU_SYM_MODE_t mode; +}; +typedef struct TPMT_SYM_DEF_OBJECT TPMT_SYM_DEF_OBJECT_t; + +/* TPMS_SYMCIPHER_PARMS Structure */ +struct TPMS_SYMCIPHER_PARMS +{ + TPMT_SYM_DEF_OBJECT_t sym; +}; +typedef struct TPMS_SYMCIPHER_PARMS TPMS_SYMCIPHER_PARMS_t; + +/* TPMU_ASYM_SCHEME Union */ +union TPMU_ASYM_SCHEME +{ + TPMS_KEY_SCHEME_ECDH_t ecdh; + TPMS_KEY_SCHEME_ECMQV_t ecmqv; + TPMS_SIG_SCHEME_RSASSA_t rsassa; + TPMS_SIG_SCHEME_RSAPSS_t rsapss; + TPMS_SIG_SCHEME_ECDSA_t ecdsa; + TPMS_SIG_SCHEME_ECDAA_t ecdaa; + TPMS_SIG_SCHEME_SM2_t sm2; + TPMS_SIG_SCHEME_ECSCHNORR_t ecschnorr; + TPMS_ENC_SCHEME_RSAES_t rsaes; + TPMS_ENC_SCHEME_OAEP_t oaep; + TPMS_SCHEME_HASH_t anySig; + unsigned char padding[4]; +}; +typedef union TPMU_ASYM_SCHEME TPMU_ASYM_SCHEME_t; + +/* TPMT_RSA_SCHEME Structure */ +struct TPMT_RSA_SCHEME +{ + TPMI_ALG_RSA_SCHEME_t scheme; + TPMU_ASYM_SCHEME_t details; +}; +typedef struct TPMT_RSA_SCHEME TPMT_RSA_SCHEME_t; + +/* TPMS_RSA_PARMS Structure */ +struct TPMS_RSA_PARMS +{ + TPMT_SYM_DEF_OBJECT_t symmetric; + TPMT_RSA_SCHEME_t scheme; + TPM_KEY_BITS_t keyBits; + grub_uint32_t exponent; +}; +typedef struct TPMS_RSA_PARMS TPMS_RSA_PARMS_t; + +/* TPMT_ECC_SCHEME Structure */ +struct TPMT_ECC_SCHEME +{ + TPMI_ALG_ECC_SCHEME_t scheme; + TPMU_ASYM_SCHEME_t details; +}; +typedef struct TPMT_ECC_SCHEME TPMT_ECC_SCHEME_t; + +/* TPMU_KDF_SCHEME Union */ +union TPMU_KDF_SCHEME +{ + TPMS_SCHEME_MGF1_t mgf1; + TPMS_SCHEME_KDF1_SP800_56A_t kdf1_sp800_56a; + TPMS_SCHEME_KDF2_t kdf2; + TPMS_SCHEME_KDF1_SP800_108_t kdf1_sp800_108; +}; +typedef union TPMU_KDF_SCHEME TPMU_KDF_SCHEME_t; + +/* TPMT_KDF_SCHEME Structure */ +struct TPMT_KDF_SCHEME +{ + TPMI_ALG_KDF_t scheme; + TPMU_KDF_SCHEME_t details; +}; +typedef struct TPMT_KDF_SCHEME TPMT_KDF_SCHEME_t; + +/* TPMS_ECC_PARMS Structure */ +struct TPMS_ECC_PARMS +{ + TPMT_SYM_DEF_OBJECT_t symmetric; + TPMT_ECC_SCHEME_t scheme; + TPMI_ECC_CURVE_t curveID; + TPMT_KDF_SCHEME_t kdf; +}; +typedef struct TPMS_ECC_PARMS TPMS_ECC_PARMS_t; + +/* TPMT_ASYM_SCHEME Structure */ +struct TPMT_ASYM_SCHEME +{ + TPMI_ALG_ASYM_SCHEME_t scheme; + TPMU_ASYM_SCHEME_t details; +}; +typedef struct TPMT_ASYM_SCHEME TPMT_ASYM_SCHEME_t; + +/* TPMS_ASYM_PARMS Structure */ +struct TPMS_ASYM_PARMS +{ + TPMT_SYM_DEF_OBJECT_t symmetric; + TPMT_ASYM_SCHEME_t scheme; +}; +typedef struct TPMS_ASYM_PARMS TPMS_ASYM_PARMS_t; + +/* TPMU_PUBLIC_PARMS Union */ +union TPMU_PUBLIC_PARMS +{ + TPMS_KEYEDHASH_PARMS_t keyedHashDetail; + TPMS_SYMCIPHER_PARMS_t symDetail; + TPMS_RSA_PARMS_t rsaDetail; + TPMS_ECC_PARMS_t eccDetail; + TPMS_ASYM_PARMS_t asymDetail; +}; +typedef union TPMU_PUBLIC_PARMS TPMU_PUBLIC_PARMS_t; + +/* TPMT_PUBLIC_PARMS Structure */ +struct TPMT_PUBLIC_PARMS { + TPMI_ALG_PUBLIC_t type; + TPMU_PUBLIC_PARMS_t parameters; +}; +typedef struct TPMT_PUBLIC_PARMS TPMT_PUBLIC_PARMS_t; + +/* TPM2B_PUBLIC_KEY_RSA Structure */ +struct TPM2B_PUBLIC_KEY_RSA +{ + grub_uint16_t size; + grub_uint8_t buffer[TPM_MAX_RSA_KEY_BYTES]; +}; +typedef struct TPM2B_PUBLIC_KEY_RSA TPM2B_PUBLIC_KEY_RSA_t; + +/* TPM2B_ECC_PARAMETER Structure */ +struct TPM2B_ECC_PARAMETER +{ + grub_uint16_t size; + grub_uint8_t buffer[TPM_MAX_ECC_KEY_BYTES]; +}; +typedef struct TPM2B_ECC_PARAMETER TPM2B_ECC_PARAMETER_t; + +/* TPMS_ECC_POINT Structure */ +struct TPMS_ECC_POINT +{ + TPM2B_ECC_PARAMETER_t x; + TPM2B_ECC_PARAMETER_t y; +}; +typedef struct TPMS_ECC_POINT TPMS_ECC_POINT_t; + +/* TPMU_ENCRYPTED_SECRET Union */ +union TPMU_ENCRYPTED_SECRET +{ + grub_uint8_t ecc[sizeof(TPMS_ECC_POINT_t)]; + grub_uint8_t rsa[TPM_MAX_RSA_KEY_BYTES]; + grub_uint8_t symmetric[sizeof(TPM2B_DIGEST_t)]; + grub_uint8_t keyedHash[sizeof(TPM2B_DIGEST_t)]; +}; +typedef union TPMU_ENCRYPTED_SECRET TPMU_ENCRYPTED_SECRET_t; + +/* TPM2B_ENCRYPTED_SECRET Structure */ +struct TPM2B_ENCRYPTED_SECRET +{ + grub_uint16_t size; + grub_uint8_t secret[sizeof(TPMU_ENCRYPTED_SECRET_t)]; +}; +typedef struct TPM2B_ENCRYPTED_SECRET TPM2B_ENCRYPTED_SECRET_t; + +/* TPMU_PUBLIC_ID Union */ +union TPMU_PUBLIC_ID +{ + TPM2B_DIGEST_t keyedHash; + TPM2B_DIGEST_t sym; + TPM2B_PUBLIC_KEY_RSA_t rsa; + TPMS_ECC_POINT_t ecc; +}; +typedef union TPMU_PUBLIC_ID TPMU_PUBLIC_ID_t; + +/* TPMT_PUBLIC Structure */ +struct TPMT_PUBLIC +{ + TPMI_ALG_PUBLIC_t type; + TPMI_ALG_HASH_t nameAlg; + TPMA_OBJECT_t objectAttributes; + TPM2B_DIGEST_t authPolicy; + TPMU_PUBLIC_PARMS_t parameters; + TPMU_PUBLIC_ID_t unique; +}; +typedef struct TPMT_PUBLIC TPMT_PUBLIC_t; + +/* TPM2B_PUBLIC Structure */ +struct TPM2B_PUBLIC +{ + grub_uint16_t size; + TPMT_PUBLIC_t publicArea; +}; +typedef struct TPM2B_PUBLIC TPM2B_PUBLIC_t; + +/* TPMT_HA Structure */ +struct TPMT_HA +{ + TPMI_ALG_HASH_t hashAlg; + TPMU_HA_t digest; +}; +typedef struct TPMT_HA TPMT_HA_t; + +/* TPM2B_DATA Structure */ +struct TPM2B_DATA +{ + grub_uint16_t size; + grub_uint8_t buffer[sizeof(TPMT_HA_t)]; +}; +typedef struct TPM2B_DATA TPM2B_DATA_t; + +/* TPMA_LOCALITY Structure */ +struct TPMA_LOCALITY +{ + grub_uint8_t TPM_LOC_ZERO:1; + grub_uint8_t TPM_LOC_ONE:1; + grub_uint8_t TPM_LOC_TWO:1; + grub_uint8_t TPM_LOC_THREE:1; + grub_uint8_t TPM_LOC_FOUR:1; + grub_uint8_t Extended:3; +}; +typedef struct TPMA_LOCALITY TPMA_LOCALITY_t; + +/* TPMU_NAME Union */ +union TPMU_NAME +{ + TPMT_HA_t digest; + TPM_HANDLE_t handle; +}; +typedef union TPMU_NAME TPMU_NAME_t; + +/* TPM2B_NAME Structure */ +struct TPM2B_NAME +{ + grub_uint16_t size; + grub_uint8_t name[sizeof(TPMU_NAME_t)]; +}; +typedef struct TPM2B_NAME TPM2B_NAME_t; + +/* TPMS_CREATION_DATA Structure */ +struct TPMS_CREATION_DATA +{ + TPML_PCR_SELECTION_t pcrSelect; + TPM2B_DIGEST_t pcrDigest; + TPMA_LOCALITY_t locality; + TPM_ALG_ID_t parentNameAlg; + TPM2B_NAME_t parentName; + TPM2B_NAME_t parentQualifiedName; + TPM2B_DATA_t outsideInfo; +}; +typedef struct TPMS_CREATION_DATA TPMS_CREATION_DATA_t; + +/* TPM2B_CREATION_DATA Structure */ +struct TPM2B_CREATION_DATA +{ + grub_uint16_t size; + TPMS_CREATION_DATA_t creationData; +}; +typedef struct TPM2B_CREATION_DATA TPM2B_CREATION_DATA_t; + +/* TPMT_SYM_DEF Structure */ +struct TPMT_SYM_DEF +{ + TPMI_ALG_SYM_t algorithm; + TPMU_SYM_KEY_BITS_t keyBits; + TPMU_SYM_MODE_t mode; +}; +typedef struct TPMT_SYM_DEF TPMT_SYM_DEF_t; + +/* TPM2B_MAX_BUFFER Structure */ +struct TPM2B_MAX_BUFFER +{ + grub_uint16_t size; + grub_uint8_t buffer[TPM_MAX_DIGEST_BUFFER]; +}; +typedef struct TPM2B_MAX_BUFFER TPM2B_MAX_BUFFER_t; + +/* TPMT_TK_HASHCHECK Structure */ +struct TPMT_TK_HASHCHECK +{ + TPM_ST_t tag; + TPMI_RH_HIERARCHY_t hierarchy; + TPM2B_DIGEST_t digest; +}; +typedef struct TPMT_TK_HASHCHECK TPMT_TK_HASHCHECK_t; + +/* TPM2B_SYM_KEY Structure */ +struct TPM2B_SYM_KEY +{ + grub_uint16_t size; + grub_uint8_t buffer[TPM_MAX_SYM_KEY_BYTES]; +}; +typedef struct TPM2B_SYM_KEY TPM2B_SYM_KEY_t; + +/* TPM2B_PRIVATE_KEY_RSA Structure */ +struct TPM2B_PRIVATE_KEY_RSA +{ + grub_uint16_t size; + grub_uint8_t buffer[TPM_MAX_RSA_KEY_BYTES/2]; +}; +typedef struct TPM2B_PRIVATE_KEY_RSA TPM2B_PRIVATE_KEY_RSA_t; + +/* TPM2B_PRIVATE_VENDOR_SPECIFIC Structure */ +struct TPM2B_PRIVATE_VENDOR_SPECIFIC +{ + grub_uint16_t size; + grub_uint8_t buffer[TPM_PRIVATE_VENDOR_SPECIFIC_BYTES]; +}; +typedef struct TPM2B_PRIVATE_VENDOR_SPECIFIC TPM2B_PRIVATE_VENDOR_SPECIFIC_t; + +/* TPM2B_PRIVATE_VENDOR_SPECIFIC Union */ +union TPMU_SENSITIVE_COMPOSITE +{ + TPM2B_PRIVATE_KEY_RSA_t rsa; + TPM2B_ECC_PARAMETER_t ecc; + TPM2B_SENSITIVE_DATA_t bits; + TPM2B_SYM_KEY_t sym; + TPM2B_PRIVATE_VENDOR_SPECIFIC_t any; +}; +typedef union TPMU_SENSITIVE_COMPOSITE TPMU_SENSITIVE_COMPOSITE_t; + +/* TPMT_SENSITIVE Structure */ +struct TPMT_SENSITIVE +{ + TPMI_ALG_PUBLIC_t sensitiveType; + TPM2B_AUTH_t authValue; + TPM2B_DIGEST_t seedValue; + TPMU_SENSITIVE_COMPOSITE_t sensitive; +}; +typedef struct TPMT_SENSITIVE TPMT_SENSITIVE_t; + +/* TPM2B_SENSITIVE Structure */ +struct TPM2B_SENSITIVE +{ + grub_uint16_t size; + TPMT_SENSITIVE_t sensitiveArea; +}; +typedef struct TPM2B_SENSITIVE TPM2B_SENSITIVE_t; + +/* + * _PRIVATE Structure + * + * Although '_PRIVATE' is the name defined in the TPM2 SPEC, it is too generic, + * so here we add the '__TPM2B' prefix to make the struct specific for 'TPM2B_PRIVATE'. + */ +struct __TPM2B_PRIVATE +{ + TPM2B_DIGEST_t integrityOuter; + TPM2B_DIGEST_t integrityInner; + TPM2B_SENSITIVE_t sensitive; +}; +typedef struct __TPM2B_PRIVATE __TPM2B_PRIVATE_t; + +/* TPM2B_PRIVATE Structure */ +struct TPM2B_PRIVATE +{ + grub_uint16_t size; + grub_uint8_t buffer[sizeof(__TPM2B_PRIVATE_t)]; +}; +typedef struct TPM2B_PRIVATE TPM2B_PRIVATE_t; + +/* TPML_DIGEST_VALUES Structure */ +struct TPML_DIGEST_VALUES +{ + grub_uint32_t count; + TPMT_HA_t digests[TPM_NUM_PCR_BANKS]; +}; +typedef struct TPML_DIGEST_VALUES TPML_DIGEST_VALUES_t; + +/* TPM2B_MAX_NV_BUFFER Structure */ +struct TPM2B_MAX_NV_BUFFER +{ + grub_uint16_t size; + grub_uint8_t buffer[TPM_MAX_NV_BUFFER_SIZE]; +}; +typedef struct TPM2B_MAX_NV_BUFFER TPM2B_MAX_NV_BUFFER_t; + +/* TPMS_NV_PUBLIC Structure */ +struct TPMS_NV_PUBLIC +{ + TPMI_RH_NV_INDEX_t nvIndex; + TPMI_ALG_HASH_t nameAlg; + TPMA_NV_t attributes; + TPM2B_DIGEST_t authPolicy; + grub_uint16_t dataSize; +}; +typedef struct TPMS_NV_PUBLIC TPMS_NV_PUBLIC_t; + +/* TPM2B_NV_PUBLIC Structure */ +struct TPM2B_NV_PUBLIC +{ + grub_uint16_t size; + TPMS_NV_PUBLIC_t nvPublic; +}; +typedef struct TPM2B_NV_PUBLIC TPM2B_NV_PUBLIC_t; + +/* TPMT_TK_CREATION Structure */ +struct TPMT_TK_CREATION +{ + TPM_ST_t tag; + TPMI_RH_HIERARCHY_t hierarchy; + TPM2B_DIGEST_t digest; +}; +typedef struct TPMT_TK_CREATION TPMT_TK_CREATION_t; + +/* TPMS_EMPTY Structure */ +struct TPMS_EMPTY { + grub_uint8_t empty[1]; /* a structure with no member */ +}; +typedef struct TPMS_EMPTY TPMS_EMPTY_t; + +/* TPMS_SIGNATURE_RSA Structure */ +struct TPMS_SIGNATURE_RSA { + TPMI_ALG_HASH_t hash; + TPM2B_PUBLIC_KEY_RSA_t sig; +}; +typedef struct TPMS_SIGNATURE_RSA TPMS_SIGNATURE_RSA_t; + +/* Definition of Types for RSA Signature */ +typedef TPMS_SIGNATURE_RSA_t TPMS_SIGNATURE_RSASSA_t; +typedef TPMS_SIGNATURE_RSA_t TPMS_SIGNATURE_RSAPSS_t; + +/* TPMS_SIGNATURE_ECC Structure */ +struct TPMS_SIGNATURE_ECC { + TPMI_ALG_HASH_t hash; + TPM2B_ECC_PARAMETER_t signatureR; + TPM2B_ECC_PARAMETER_t signatureS; +}; +typedef struct TPMS_SIGNATURE_ECC TPMS_SIGNATURE_ECC_t; + +/* Definition of Types for ECC TPMS_SIGNATURE_ECC */ +typedef TPMS_SIGNATURE_ECC_t TPMS_SIGNATURE_ECDSA_t; +typedef TPMS_SIGNATURE_ECC_t TPMS_SIGNATURE_ECDAA_t; +typedef TPMS_SIGNATURE_ECC_t TPMS_SIGNATURE_SM2_t; +typedef TPMS_SIGNATURE_ECC_t TPMS_SIGNATURE_ECSCHNORR_t; + +/* TPMU_SIGNATURE Structure */ +union TPMU_SIGNATURE { + TPMS_SIGNATURE_RSASSA_t rsassa; + TPMS_SIGNATURE_RSAPSS_t rsapss; + TPMS_SIGNATURE_ECDSA_t ecdsa; + TPMS_SIGNATURE_ECDAA_t ecdaa; + TPMS_SIGNATURE_SM2_t sm2; + TPMS_SIGNATURE_ECSCHNORR_t ecschnorr; + TPMT_HA_t hmac; + TPMS_SCHEME_HASH_t any; + TPMS_EMPTY_t null; +}; +typedef union TPMU_SIGNATURE TPMU_SIGNATURE_t; + +/* TPMT_SIGNATURE Structure */ +struct TPMT_SIGNATURE { + TPMI_ALG_SIG_SCHEME_t sigAlg; + TPMU_SIGNATURE_t signature; +}; +typedef struct TPMT_SIGNATURE TPMT_SIGNATURE_t; + +static inline TPMI_ALG_HASH_t +TPMT_SIGNATURE_get_hash_alg (TPMT_SIGNATURE_t *sig) +{ + switch (sig->sigAlg) + { + case TPM_ALG_RSASSA: + return sig->signature.rsassa.hash; + case TPM_ALG_RSAPSS: + return sig->signature.rsapss.hash; + case TPM_ALG_ECDSA: + return sig->signature.ecdsa.hash; + case TPM_ALG_ECDAA: + return sig->signature.ecdaa.hash; + case TPM_ALG_SM2: + return sig->signature.sm2.hash; + case TPM_ALG_ECSCHNORR: + return sig->signature.ecschnorr.hash; + case TPM_ALG_HMAC: + return sig->signature.hmac.hashAlg; + default: + break; + } + + return TPM_ALG_NULL; +} + +/* TPMT_TK_VERIFIED Structure */ +struct TPMT_TK_VERIFIED { + TPM_ST_t tag; + TPMI_RH_HIERARCHY_t hierarchy; + TPM2B_DIGEST_t digest; +}; +typedef struct TPMT_TK_VERIFIED TPMT_TK_VERIFIED_t; + +#endif /* ! GRUB_TPM2_INTERNAL_STRUCTS_HEADER */ diff --git a/grub-core/lib/tss2/tss2_types.h b/grub-core/lib/tss2/tss2_types.h new file mode 100644 index 000000000..5b1a7947d --- /dev/null +++ b/grub-core/lib/tss2/tss2_types.h @@ -0,0 +1,404 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 GRUB_TPM2_INTERNAL_TYPES_HEADER +#define GRUB_TPM2_INTERNAL_TYPES_HEADER 1 + +#include + +/* TPM2_RC Constants */ +typedef grub_uint32_t TPM_RC_t; + +#define TPM_RC_1 ((TPM_RC_t) 0x100) +#define TPM_RC_2 ((TPM_RC_t) 0x200) +#define TPM_RC_3 ((TPM_RC_t) 0x300) +#define TPM_RC_4 ((TPM_RC_t) 0x400) +#define TPM_RC_5 ((TPM_RC_t) 0x500) +#define TPM_RC_6 ((TPM_RC_t) 0x600) +#define TPM_RC_7 ((TPM_RC_t) 0x700) +#define TPM_RC_8 ((TPM_RC_t) 0x800) +#define TPM_RC_9 ((TPM_RC_t) 0x900) +#define TPM_RC_A ((TPM_RC_t) 0xA00) +#define TPM_RC_ASYMMETRIC ((TPM_RC_t) 0x081) +#define TPM_RC_ATTRIBUTES ((TPM_RC_t) 0x082) +#define TPM_RC_AUTH_CONTEXT ((TPM_RC_t) 0x145) +#define TPM_RC_AUTH_FAIL ((TPM_RC_t) 0x08E) +#define TPM_RC_AUTH_MISSING ((TPM_RC_t) 0x125) +#define TPM_RC_AUTHSIZE ((TPM_RC_t) 0x144) +#define TPM_RC_AUTH_TYPE ((TPM_RC_t) 0x124) +#define TPM_RC_AUTH_UNAVAILABLE ((TPM_RC_t) 0x12F) +#define TPM_RC_B ((TPM_RC_t) 0xB00) +#define TPM_RC_BAD_AUTH ((TPM_RC_t) 0x0A2) +#define TPM_RC_BAD_CONTEXT ((TPM_RC_t) 0x150) +#define TPM_RC_BAD_TAG ((TPM_RC_t) 0x01E) +#define TPM_RC_BINDING ((TPM_RC_t) 0x0A5) +#define TPM_RC_C ((TPM_RC_t) 0xC00) +#define TPM_RC_CANCELED ((TPM_RC_t) 0x909) +#define TPM_RC_COMMAND_CODE ((TPM_RC_t) 0x143) +#define TPM_RC_COMMAND_SIZE ((TPM_RC_t) 0x142) +#define TPM_RC_CONTEXT_GAP ((TPM_RC_t) 0x901) +#define TPM_RC_CPHASH ((TPM_RC_t) 0x151) +#define TPM_RC_CURVE ((TPM_RC_t) 0x0A6) +#define TPM_RC_D ((TPM_RC_t) 0xD00) +#define TPM_RC_DISABLED ((TPM_RC_t) 0x120) +#define TPM_RC_E ((TPM_RC_t) 0xE00) +#define TPM_RC_ECC_POINT ((TPM_RC_t) 0x0A7) +#define TPM_RC_EXCLUSIVE ((TPM_RC_t) 0x121) +#define TPM_RC_EXPIRED ((TPM_RC_t) 0x0A3) +#define TPM_RC_F ((TPM_RC_t) 0xF00) +#define TPM_RC_FAILURE ((TPM_RC_t) 0x101) +#define TPM_RC_H ((TPM_RC_t) 0x000) +#define TPM_RC_HANDLE ((TPM_RC_t) 0x08B) +#define TPM_RC_HASH ((TPM_RC_t) 0x083) +#define TPM_RC_HIERARCHY ((TPM_RC_t) 0x085) +#define TPM_RC_HMAC ((TPM_RC_t) 0x119) +#define TPM_RC_INITIALIZE ((TPM_RC_t) 0x100) +#define TPM_RC_INSUFFICIENT ((TPM_RC_t) 0x09A) +#define TPM_RC_INTEGRITY ((TPM_RC_t) 0x09F) +#define TPM_RC_KDF ((TPM_RC_t) 0x08C) +#define TPM_RC_KEY ((TPM_RC_t) 0x09C) +#define TPM_RC_KEY_SIZE ((TPM_RC_t) 0x087) +#define TPM_RC_LOCALITY ((TPM_RC_t) 0x907) +#define TPM_RC_LOCKOUT ((TPM_RC_t) 0x921) +#define TPM_RC_MEMORY ((TPM_RC_t) 0x904) +#define TPM_RC_MGF ((TPM_RC_t) 0x088) +#define TPM_RC_MODE ((TPM_RC_t) 0x089) +#define TPM_RC_NEEDS_TEST ((TPM_RC_t) 0x153) +#define TPM_RC_N_MASK ((TPM_RC_t) 0xF00) +#define TPM_RC_NONCE ((TPM_RC_t) 0x08F) +#define TPM_RC_NO_RESULT ((TPM_RC_t) 0x154) +#define TPM_RC_NOT_USED ((TPM_RC_t) 0x97F) +#define TPM_RC_NV_AUTHORIZATION ((TPM_RC_t) 0x149) +#define TPM_RC_NV_DEFINED ((TPM_RC_t) 0x14C) +#define TPM_RC_NV_LOCKED ((TPM_RC_t) 0x148) +#define TPM_RC_NV_RANGE ((TPM_RC_t) 0x146) +#define TPM_RC_NV_RATE ((TPM_RC_t) 0x920) +#define TPM_RC_NV_SIZE ((TPM_RC_t) 0x147) +#define TPM_RC_NV_SPACE ((TPM_RC_t) 0x14B) +#define TPM_RC_NV_UNAVAILABLE ((TPM_RC_t) 0x923) +#define TPM_RC_NV_UNINITIALIZED ((TPM_RC_t) 0x14A) +#define TPM_RC_OBJECT_HANDLES ((TPM_RC_t) 0x906) +#define TPM_RC_OBJECT_MEMORY ((TPM_RC_t) 0x902) +#define TPM_RC_P ((TPM_RC_t) 0x040) +#define TPM_RC_PARENT ((TPM_RC_t) 0x152) +#define TPM_RC_PCR ((TPM_RC_t) 0x127) +#define TPM_RC_PCR_CHANGED ((TPM_RC_t) 0x128) +#define TPM_RC_POLICY ((TPM_RC_t) 0x126) +#define TPM_RC_POLICY_CC ((TPM_RC_t) 0x0A4) +#define TPM_RC_POLICY_FAIL ((TPM_RC_t) 0x09D) +#define TPM_RC_PP ((TPM_RC_t) 0x090) +#define TPM_RC_PRIVATE ((TPM_RC_t) 0x10B) +#define TPM_RC_RANGE ((TPM_RC_t) 0x08D) +#define TPM_RC_REBOOT ((TPM_RC_t) 0x130) +#define TPM_RC_REFERENCE_H0 ((TPM_RC_t) 0x910) +#define TPM_RC_REFERENCE_H1 ((TPM_RC_t) 0x911) +#define TPM_RC_REFERENCE_H2 ((TPM_RC_t) 0x912) +#define TPM_RC_REFERENCE_H3 ((TPM_RC_t) 0x913) +#define TPM_RC_REFERENCE_H4 ((TPM_RC_t) 0x914) +#define TPM_RC_REFERENCE_H5 ((TPM_RC_t) 0x915) +#define TPM_RC_REFERENCE_H6 ((TPM_RC_t) 0x916) +#define TPM_RC_REFERENCE_S0 ((TPM_RC_t) 0x918) +#define TPM_RC_REFERENCE_S1 ((TPM_RC_t) 0x919) +#define TPM_RC_REFERENCE_S2 ((TPM_RC_t) 0x91A) +#define TPM_RC_REFERENCE_S3 ((TPM_RC_t) 0x91B) +#define TPM_RC_REFERENCE_S4 ((TPM_RC_t) 0x91C) +#define TPM_RC_REFERENCE_S5 ((TPM_RC_t) 0x91D) +#define TPM_RC_REFERENCE_S6 ((TPM_RC_t) 0x91E) +#define TPM_RC_RESERVED_BITS ((TPM_RC_t) 0x0A1) +#define TPM_RC_RETRY ((TPM_RC_t) 0x922) +#define TPM_RC_S ((TPM_RC_t) 0x800) +#define TPM_RC_SCHEME ((TPM_RC_t) 0x092) +#define TPM_RC_SELECTOR ((TPM_RC_t) 0x098) +#define TPM_RC_SENSITIVE ((TPM_RC_t) 0x155) +#define TPM_RC_SEQUENCE ((TPM_RC_t) 0x103) +#define TPM_RC_SESSION_HANDLES ((TPM_RC_t) 0x905) +#define TPM_RC_SESSION_MEMORY ((TPM_RC_t) 0x903) +#define TPM_RC_SIGNATURE ((TPM_RC_t) 0x09B) +#define TPM_RC_SIZE ((TPM_RC_t) 0x095) +#define TPM_RC_SUCCESS ((TPM_RC_t) 0x000) +#define TPM_RC_SYMMETRIC ((TPM_RC_t) 0x096) +#define TPM_RC_TAG ((TPM_RC_t) 0x097) +#define TPM_RC_TESTING ((TPM_RC_t) 0x90A) +#define TPM_RC_TICKET ((TPM_RC_t) 0x0A0) +#define TPM_RC_TOO_MANY_CONTEXTS ((TPM_RC_t) 0x12E) +#define TPM_RC_TYPE ((TPM_RC_t) 0x08A) +#define TPM_RC_UNBALANCED ((TPM_RC_t) 0x131) +#define TPM_RC_UPGRADE ((TPM_RC_t) 0x12D) +#define TPM_RC_VALUE ((TPM_RC_t) 0x084) +#define TPM_RC_YIELDED ((TPM_RC_t) 0x908) + +/* TPMA_NV_t Constants */ +typedef grub_uint32_t TPMA_NV_t; + +#define TPMA_NV_PPWRITE ((TPMA_NV_t) 0x00000001) +#define TPMA_NV_OWNERWRITE ((TPMA_NV_t) 0x00000002) +#define TPMA_NV_AUTHWRITE ((TPMA_NV_t) 0x00000004) +#define TPMA_NV_POLICYWRITE ((TPMA_NV_t) 0x00000008) +#define TPMA_NV_TPM2_NT_MASK ((TPMA_NV_t) 0x000000F0) +#define TPMA_NV_TPM2_NT_SHIFT (4) +#define TPMA_NV_RESERVED1_MASK ((TPMA_NV_t) 0x00000300) +#define TPMA_NV_POLICY_DELETE ((TPMA_NV_t) 0x00000400) +#define TPMA_NV_WRITELOCKED ((TPMA_NV_t) 0x00000800) +#define TPMA_NV_WRITEALL ((TPMA_NV_t) 0x00001000) +#define TPMA_NV_WRITEDEFINE ((TPMA_NV_t) 0x00002000) +#define TPMA_NV_WRITE_STCLEAR ((TPMA_NV_t) 0x00004000) +#define TPMA_NV_GLOBALLOCK ((TPMA_NV_t) 0x00008000) +#define TPMA_NV_PPREAD ((TPMA_NV_t) 0x00010000) +#define TPMA_NV_OWNERREAD ((TPMA_NV_t) 0x00020000) +#define TPMA_NV_AUTHREAD ((TPMA_NV_t) 0x00040000) +#define TPMA_NV_POLICYREAD ((TPMA_NV_t) 0x00080000) +#define TPMA_NV_RESERVED2_MASK ((TPMA_NV_t) 0x01F00000) +#define TPMA_NV_NO_DA ((TPMA_NV_t) 0x02000000) +#define TPMA_NV_ORDERLY ((TPMA_NV_t) 0x04000000) +#define TPMA_NV_CLEAR_STCLEAR ((TPMA_NV_t) 0x08000000) +#define TPMA_NV_READLOCKED ((TPMA_NV_t) 0x10000000) +#define TPMA_NV_WRITTEN ((TPMA_NV_t) 0x20000000) +#define TPMA_NV_PLATFORMCREATE ((TPMA_NV_t) 0x40000000) +#define TPMA_NV_READ_STCLEAR ((TPMA_NV_t) 0x80000000) + +/* TPM_ALG_ID_t Constants */ +typedef grub_uint16_t TPM_ALG_ID_t; + +#define TPM_ALG_ERROR ((TPM_ALG_ID_t) 0x0000) +#define TPM_ALG_AES ((TPM_ALG_ID_t) 0x0006) +#define TPM_ALG_CAMELLIA ((TPM_ALG_ID_t) 0x0026) +#define TPM_ALG_CBC ((TPM_ALG_ID_t) 0x0042) +#define TPM_ALG_CFB ((TPM_ALG_ID_t) 0x0043) +#define TPM_ALG_ECB ((TPM_ALG_ID_t) 0x0044) +#define TPM_ALG_ECC ((TPM_ALG_ID_t) 0x0023) +#define TPM_ALG_ECDAA ((TPM_ALG_ID_t) 0x001A) +#define TPM_ALG_ECDSA ((TPM_ALG_ID_t) 0x0018) +#define TPM_ALG_ECSCHNORR ((TPM_ALG_ID_t) 0x001C) +#define TPM_ALG_HMAC ((TPM_ALG_ID_t) 0x0005) +#define TPM_ALG_KDF1_SP800_108 ((TPM_ALG_ID_t) 0x0022) +#define TPM_ALG_KDF1_SP800_56A ((TPM_ALG_ID_t) 0x0020) +#define TPM_ALG_KDF2 ((TPM_ALG_ID_t) 0x0021) +#define TPM_ALG_KEYEDHASH ((TPM_ALG_ID_t) 0x0008) +#define TPM_ALG_MGF1 ((TPM_ALG_ID_t) 0x0007) +#define TPM_ALG_NULL ((TPM_ALG_ID_t) 0x0010) +#define TPM_ALG_RSA ((TPM_ALG_ID_t) 0x0001) +#define TPM_ALG_RSASSA ((TPM_ALG_ID_t) 0x0014) +#define TPM_ALG_RSAPSS ((TPM_ALG_ID_t) 0x0016) +#define TPM_ALG_SHA1 ((TPM_ALG_ID_t) 0x0004) +#define TPM_ALG_SHA256 ((TPM_ALG_ID_t) 0x000B) +#define TPM_ALG_SHA384 ((TPM_ALG_ID_t) 0x000C) +#define TPM_ALG_SHA512 ((TPM_ALG_ID_t) 0x000D) +#define TPM_ALG_SM2 ((TPM_ALG_ID_t) 0x001B) +#define TPM_ALG_SM3_256 ((TPM_ALG_ID_t) 0x0012) +#define TPM_ALG_SM4 ((TPM_ALG_ID_t) 0x0013) +#define TPM_ALG_SYMCIPHER ((TPM_ALG_ID_t) 0x0025) +#define TPM_ALG_XOR ((TPM_ALG_ID_t) 0x000A) + +/* TPM_CAP_t Constants */ +typedef grub_uint32_t TPM_CAP_t; + +#define TPM_CAP_FIRST ((TPM_CAP_t) 0x00000000) +#define TPM_CAP_ALGS ((TPM_CAP_t) 0x00000000) +#define TPM_CAP_HANDLES ((TPM_CAP_t) 0x00000001) +#define TPM_CAP_COMMANDS ((TPM_CAP_t) 0x00000002) +#define TPM_CAP_PP_COMMANDS ((TPM_CAP_t) 0x00000003) +#define TPM_CAP_AUDIT_COMMANDS ((TPM_CAP_t) 0x00000004) +#define TPM_CAP_PCRS ((TPM_CAP_t) 0x00000005) +#define TPM_CAP_TPM_PROPERTIES ((TPM_CAP_t) 0x00000006) +#define TPM_CAP_PCR_PROPERTIES ((TPM_CAP_t) 0x00000007) +#define TPM_CAP_ECC_CURVES ((TPM_CAP_t) 0x00000008) +#define TPM_CAP_LAST ((TPM_CAP_t) 0x00000008) +#define TPM_CAP_VENDOR_PROPERTY ((TPM_CAP_t) 0x00000100) + +/* TPM_PT_t Constants */ +typedef grub_uint32_t TPM_PT_t; + +#define TPM_PT_NONE ((TPM_PT_t) 0x00000000) +#define PT_GROUP ((TPM_PT_t) 0x00000100) +#define PT_FIXED ((TPM_PT_t) (PT_GROUP * 1)) +#define TPM_PT_FAMILY_INDICATOR ((TPM_PT_t) (PT_FIXED + 0)) +#define TPM_PT_LEVEL ((TPM_PT_t) (PT_FIXED + 1)) +#define TPM_PT_REVISION ((TPM_PT_t) (PT_FIXED + 2)) +#define TPM_PT_DAY_OF_YEAR ((TPM_PT_t) (PT_FIXED + 3)) +#define TPM_PT_YEAR ((TPM_PT_t) (PT_FIXED + 4)) +#define TPM_PT_PCR_COUNT ((TPM_PT_t) (PT_FIXED + 18)) + +/* TPM_SE_t Constants */ +typedef grub_uint8_t TPM_SE_t; + +#define TPM_SE_HMAC ((TPM_SE_t) 0x00) +#define TPM_SE_POLICY ((TPM_SE_t) 0x01) +#define TPM_SE_TRIAL ((TPM_SE_t) 0x03) + +/* TPMI_YES_NO_t Constants */ +typedef grub_uint8_t TPMI_YES_NO_t; + +#define TPM_NO ((TPMI_YES_NO_t)0) +#define TPM_YES ((TPMI_YES_NO_t)1) + +/* TPM_ST_t Constants */ +typedef grub_uint16_t TPM_ST_t; +typedef TPM_ST_t TPMI_ST_COMMAND_TAG_t; + +#define TPM_ST_NO_SESSIONS ((TPMI_ST_COMMAND_TAG_t) 0x8001) +#define TPM_ST_SESSIONS ((TPMI_ST_COMMAND_TAG_t) 0x8002) + +/* TPM_HANDLE_t Types */ +typedef grub_uint32_t TPM_HANDLE_t; + +typedef TPM_HANDLE_t TPMI_RH_HIERARCHY_t; +typedef TPM_HANDLE_t TPMI_RH_LOCKOUT_t; +typedef TPM_HANDLE_t TPMI_SH_AUTH_SESSION_t; +typedef TPM_HANDLE_t TPMI_DH_CONTEXT_t; +typedef TPM_HANDLE_t TPMI_DH_OBJECT_t; +typedef TPM_HANDLE_t TPMI_DH_ENTITY_t; +typedef TPM_HANDLE_t TPMI_SH_POLICY_t; +typedef TPM_HANDLE_t TPMI_DH_PCR_t; +typedef TPM_HANDLE_t TPMI_RH_NV_AUTH_t; +typedef TPM_HANDLE_t TPMI_RH_NV_INDEX_t; + +/* TPM_HT_t Constants */ +typedef grub_uint8_t TPM_HT_t; +#define TPM_HT_PERMANENT ((TPM_HT_t) 0x40) +#define TPM_HT_PERSISTENT ((TPM_HT_t) 0x81) + +/* TPM_RH_t Constants */ +typedef TPM_HANDLE_t TPM_RH_t; + +#define TPM_RH_FIRST ((TPM_RH_t) 0x40000000) +#define TPM_RH_SRK ((TPM_RH_t) 0x40000000) +#define TPM_RH_OWNER ((TPM_RH_t) 0x40000001) +#define TPM_RH_REVOKE ((TPM_RH_t) 0x40000002) +#define TPM_RH_TRANSPORT ((TPM_RH_t) 0x40000003) +#define TPM_RH_OPERATOR ((TPM_RH_t) 0x40000004) +#define TPM_RH_ADMIN ((TPM_RH_t) 0x40000005) +#define TPM_RH_EK ((TPM_RH_t) 0x40000006) +#define TPM_RH_NULL ((TPM_RH_t) 0x40000007) +#define TPM_RH_UNASSIGNED ((TPM_RH_t) 0x40000008) +#define TPM_RS_PW ((TPM_RH_t) 0x40000009) +#define TPM_RH_LOCKOUT ((TPM_RH_t) 0x4000000A) +#define TPM_RH_ENDORSEMENT ((TPM_RH_t) 0x4000000B) +#define TPM_RH_PLATFORM ((TPM_RH_t) 0x4000000C) +#define TPM_RH_PLATFORM_NV ((TPM_RH_t) 0x4000000D) +#define TPM_RH_AUTH_00 ((TPM_RH_t) 0x40000010) +#define TPM_RH_AUTH_FF ((TPM_RH_t) 0x4000010F) +#define TPM_RH_LAST ((TPM_RH_t) 0x4000010F) + +/* TPM_HC_t Constants */ +typedef TPM_HANDLE_t TPM_HC_t; +#define TPM_HR_HANDLE_MASK ((TPM_HC_t) 0x00FFFFFF) +#define TPM_HR_RANGE_MASK ((TPM_HC_t) 0xFF000000) +#define TPM_HR_SHIFT ((TPM_HC_t) 24) +#define TPM_HR_PERSISTENT ((TPM_HC_t) (TPM_HT_PERSISTENT << TPM_HR_SHIFT)) +#define TPM_HR_PERMANENT ((TPM_HC_t) (TPM_HT_PERMANENT << TPM_HR_SHIFT)) +#define TPM_PERSISTENT_FIRST ((TPM_HC_t) (TPM_HR_PERSISTENT + 0)) +#define TPM_PERSISTENT_LAST ((TPM_HC_t) (TPM_PERSISTENT_FIRST + 0x00FFFFFF)) +#define TPM_PERMANENT_FIRST ((TPM_HC_t) TPM_RH_FIRST) +#define TPM_PERMANENT_LAST ((TPM_HC_t) TPM_RH_LAST) + +/* TPM Handle Type Checks */ +#define TPM_HT_IS_PERMANENT(HANDLE) (((HANDLE) >> TPM_HR_SHIFT) == TPM_HT_PERMANENT) +#define TPM_HT_IS_PERSISTENT(HANDLE) (((HANDLE) >> TPM_HR_SHIFT) == TPM_HT_PERSISTENT) + +/* TPM_ECC_CURVE_t Constants */ +typedef grub_uint16_t TPM_ECC_CURVE_t; + +#define TPM_ECC_NONE ((TPM_ECC_CURVE_t) 0x0000) +#define TPM_ECC_NIST_P192 ((TPM_ECC_CURVE_t) 0x0001) +#define TPM_ECC_NIST_P224 ((TPM_ECC_CURVE_t) 0x0002) +#define TPM_ECC_NIST_P256 ((TPM_ECC_CURVE_t) 0x0003) +#define TPM_ECC_NIST_P384 ((TPM_ECC_CURVE_t) 0x0004) +#define TPM_ECC_NIST_P521 ((TPM_ECC_CURVE_t) 0x0005) +#define TPM_ECC_BN_P256 ((TPM_ECC_CURVE_t) 0x0010) +#define TPM_ECC_BN_P638 ((TPM_ECC_CURVE_t) 0x0011) +#define TPM_ECC_SM2_P256 ((TPM_ECC_CURVE_t) 0x0020) + +/* TPM_CC_t Constants */ +typedef grub_uint32_t TPM_CC_t; + +#define TPM_CC_EvictControl ((TPM_CC_t) 0x00000120) +#define TPM_CC_CreatePrimary ((TPM_CC_t) 0x00000131) +#define TPM_CC_Create ((TPM_CC_t) 0x00000153) +#define TPM_CC_FlushContext ((TPM_CC_t) 0x00000165) +#define TPM_CC_ReadPublic ((TPM_CC_t) 0x00000173) +#define TPM_CC_StartAuthSession ((TPM_CC_t) 0x00000176) +#define TPM_CC_PolicyPCR ((TPM_CC_t) 0x0000017f) +#define TPM_CC_NV_Read ((TPM_CC_t) 0x0000014e) +#define TPM_CC_NV_ReadPublic ((TPM_CC_t) 0x00000169) +#define TPM_CC_GetCapability ((TPM_CC_t) 0x0000017a) +#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) +#define TPM_CC_Unseal ((TPM_CC_t) 0x0000015e) +#define TPM_CC_PolicyGetDigest ((TPM_CC_t) 0x00000189) +#define TPM_CC_Hash ((TPM_CC_t) 0x0000017d) +#define TPM_CC_VerifySignature ((TPM_CC_t) 0x00000177) +#define TPM_CC_PolicyAuthorize ((TPM_CC_t) 0x0000016a) +#define TPM_CC_TestParms ((TPM_CC_t) 0x0000018a) + +/* Hash algorithm sizes */ +#define TPM_SHA1_DIGEST_SIZE 20 +#define TPM_SHA256_DIGEST_SIZE 32 +#define TPM_SM3_256_DIGEST_SIZE 32 +#define TPM_SHA384_DIGEST_SIZE 48 +#define TPM_SHA512_DIGEST_SIZE 64 + +/* Encryption algorithm sizes */ +#define TPM_MAX_SYM_BLOCK_SIZE 16 +#define TPM_MAX_SYM_DATA 256 +#define TPM_MAX_ECC_KEY_BYTES 128 +#define TPM_MAX_SYM_KEY_BYTES 32 +#define TPM_MAX_RSA_KEY_BYTES 512 + +/* Buffer Size Constants */ +#define TPM_MAX_PCRS 24 +#define TPM_NUM_PCR_BANKS 16 +#define TPM_PCR_SELECT_MAX ((TPM_MAX_PCRS + 7) / 8) +#define TPM_MAX_DIGEST_BUFFER 1024 +#define TPM_MAX_TPM_PROPERTIES 8 +#define TPM_MAX_NV_BUFFER_SIZE 2048 +#define TPM_PRIVATE_VENDOR_SPECIFIC_BYTES 1280 + +/* TPM_GENERATED_t Constants */ +typedef grub_uint32_t TPM_GENERATED_t; + +#define TPM_GENERATED_VALUE ((TPM_GENERATED_t) 0xff544347) + +/* TPM_ALG_ID_t Types */ +typedef TPM_ALG_ID_t TPMI_ALG_PUBLIC_t; +typedef TPM_ALG_ID_t TPMI_ALG_HASH_t; +typedef TPM_ALG_ID_t TPMI_ALG_KEYEDHASH_SCHEME_t; +typedef TPM_ALG_ID_t TPMI_ALG_KDF_t; +typedef TPM_ALG_ID_t TPMI_ALG_SYM_OBJECT_t; +typedef TPM_ALG_ID_t TPMI_ALG_SYM_MODE_t; +typedef TPM_ALG_ID_t TPMI_ALG_RSA_DECRYPT_t; +typedef TPM_ALG_ID_t TPMI_ALG_ECC_SCHEME_t; +typedef TPM_ALG_ID_t TPMI_ALG_ASYM_SCHEME_t; +typedef TPM_ALG_ID_t TPMI_ALG_RSA_SCHEME_t; +typedef TPM_ALG_ID_t TPMI_ALG_SYM_t; +typedef TPM_ALG_ID_t TPMI_ALG_SIG_SCHEME_t; + +/* TPM_KEY_BITS_t Type */ +typedef grub_uint16_t TPM_KEY_BITS_t; + +/* TPMI_ECC_CURVE_t Types */ +typedef TPM_ECC_CURVE_t TPMI_ECC_CURVE_t; + +/* TPMI_RH_PROVISION_t Type */ +typedef TPM_HANDLE_t TPMI_RH_PROVISION_t; + +/* TPMI_RH_PROVISION_t Type */ +typedef TPM_HANDLE_t TPMI_DH_PERSISTENT_t; + +#endif /* ! GRUB_TPM2_INTERNAL_TYPES_HEADER */ From 35c9904df43ed4502fbfbda59da9b1b9426a6b58 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:34:50 +0800 Subject: [PATCH 231/402] tss2: Add TPM2 Software Stack (TSS2) support A Trusted Platform Module (TPM) Software Stack (TSS) provides logic to compose and submit TPM commands and parse responses. A limited number of TPM commands may be accessed via the EFI TCG2 protocol. This protocol exposes functionality that is primarily geared toward TPM usage within the context of Secure Boot. For all other TPM commands, however, such as sealing and unsealing, this protocol does not provide any help, with the exception of passthrough command submission. The SubmitCommand method allows a caller to send raw commands to the system's TPM and to receive the corresponding response. These command/response pairs are formatted using the TPM wire protocol. To construct commands in this way, and to parse the TPM's response, it is necessary to, first, possess knowledge of the various TPM structures, and, second, of the TPM wire protocol itself. As such, this patch includes implementations of various grub_tpm2_* functions (inventoried below), and logic to write and read command and response buffers, respectively, using the TPM wire protocol. Functions: - grub_tpm2_create(), - grub_tpm2_createprimary(), - grub_tpm2_evictcontrol(), - grub_tpm2_flushcontext(), - grub_tpm2_load(), - grub_tpm2_pcr_read(), - grub_tpm2_policygetdigest(), - grub_tpm2_policypcr(), - grub_tpm2_readpublic(), - grub_tpm2_startauthsession(), - grub_tpm2_unseal(), - grub_tpm2_loadexternal(), - grub_tpm2_hash(), - grub_tpm2_verifysignature(), - grub_tpm2_policyauthorize(), - grub_tpm2_testparms(). Signed-off-by: Hernan Gatta Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Reviewed-by: Stefan Berger Tested-by: Stefan Berger --- grub-core/Makefile.core.def | 11 + grub-core/lib/efi/tcg2.c | 143 +++++ grub-core/lib/tss2/tcg2.h | 35 ++ grub-core/lib/tss2/tpm2_cmd.c | 1043 +++++++++++++++++++++++++++++++++ grub-core/lib/tss2/tpm2_cmd.h | 157 +++++ grub-core/lib/tss2/tss2.c | 21 + 6 files changed, 1410 insertions(+) create mode 100644 grub-core/lib/efi/tcg2.c create mode 100644 grub-core/lib/tss2/tcg2.h create mode 100644 grub-core/lib/tss2/tpm2_cmd.c create mode 100644 grub-core/lib/tss2/tpm2_cmd.h create mode 100644 grub-core/lib/tss2/tss2.c diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 37f131ae2..45b705a34 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -2567,6 +2567,17 @@ module = { enable = efi; }; +module = { + name = tss2; + common = lib/tss2/buffer.c; + common = lib/tss2/tss2_mu.c; + common = lib/tss2/tpm2_cmd.c; + common = lib/tss2/tss2.c; + efi = lib/efi/tcg2.c; + enable = efi; + cppflags = '-I$(srcdir)/lib/tss2'; +}; + module = { name = tr; common = commands/tr.c; diff --git a/grub-core/lib/efi/tcg2.c b/grub-core/lib/efi/tcg2.c new file mode 100644 index 000000000..841bf50bb --- /dev/null +++ b/grub-core/lib/efi/tcg2.c @@ -0,0 +1,143 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 + +static grub_err_t +tcg2_get_caps (grub_efi_tpm2_protocol_t *protocol, int *tpm2, grub_size_t *max_output_size) +{ + grub_efi_status_t status; + static bool has_caps = 0; + static EFI_TCG2_BOOT_SERVICE_CAPABILITY caps = + { + .Size = (grub_uint8_t) sizeof (caps) + }; + + if (has_caps) + goto exit; + + status = protocol->get_capability (protocol, &caps); + if (status != GRUB_EFI_SUCCESS || !caps.TPMPresentFlag) + return GRUB_ERR_FILE_NOT_FOUND; + + has_caps = 1; + + exit: + if (tpm2 != NULL) + *tpm2 = caps.TPMPresentFlag; + if (max_output_size != NULL) + *max_output_size = caps.MaxResponseSize; + + return GRUB_ERR_NONE; +} + +static grub_err_t +tcg2_get_protocol (grub_efi_tpm2_protocol_t **protocol) +{ + static grub_guid_t tpm2_guid = EFI_TPM2_GUID; + static grub_efi_tpm2_protocol_t *tpm2_protocol = NULL; + int tpm2; + grub_efi_handle_t *handles; + grub_efi_uintn_t num_handles; + grub_efi_handle_t tpm2_handle; + grub_err_t err = GRUB_ERR_FILE_NOT_FOUND; + + if (tpm2_protocol != NULL) + { + *protocol = tpm2_protocol; + return GRUB_ERR_NONE; + } + + handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &tpm2_guid, NULL, + &num_handles); + if (handles == NULL || num_handles == 0) + return err; + + tpm2_handle = handles[0]; + + tpm2_protocol = grub_efi_open_protocol (tpm2_handle, &tpm2_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (tpm2_protocol == NULL) + goto exit; + + err = tcg2_get_caps (tpm2_protocol, &tpm2, NULL); + if (err != GRUB_ERR_NONE || tpm2 == 0) + goto exit; + + *protocol = tpm2_protocol; + err = GRUB_ERR_NONE; + + exit: + grub_free (handles); + return err; +} + +grub_err_t +grub_tcg2_get_max_output_size (grub_size_t *size) +{ + grub_err_t err; + grub_size_t max; + grub_efi_tpm2_protocol_t *protocol; + + if (size == NULL) + return GRUB_ERR_BAD_ARGUMENT; + + err = tcg2_get_protocol (&protocol); + if (err != GRUB_ERR_NONE) + return err; + + err = tcg2_get_caps (protocol, NULL, &max); + if (err != GRUB_ERR_NONE) + return err; + + *size = max; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_tcg2_submit_command (grub_size_t input_size, + grub_uint8_t *input, + grub_size_t output_size, + grub_uint8_t *output) +{ + grub_err_t err; + grub_efi_status_t status; + grub_efi_tpm2_protocol_t *protocol; + + if (input_size == 0 || input == NULL || + output_size == 0 || output == NULL) + return GRUB_ERR_BAD_ARGUMENT; + + err = tcg2_get_protocol (&protocol); + if (err != GRUB_ERR_NONE) + return err; + + status = protocol->submit_command (protocol, input_size, input, + output_size, output); + if (status != GRUB_EFI_SUCCESS) + return GRUB_ERR_INVALID_COMMAND; + + return GRUB_ERR_NONE; +} diff --git a/grub-core/lib/tss2/tcg2.h b/grub-core/lib/tss2/tcg2.h new file mode 100644 index 000000000..3d26373dd --- /dev/null +++ b/grub-core/lib/tss2/tcg2.h @@ -0,0 +1,35 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 GRUB_TPM2_TCG2_HEADER +#define GRUB_TPM2_TCG2_HEADER 1 + +#include +#include + +extern grub_err_t +grub_tcg2_get_max_output_size (grub_size_t *size); + +extern grub_err_t +grub_tcg2_submit_command (grub_size_t input_size, + grub_uint8_t *input, + grub_size_t output_size, + grub_uint8_t *output); + +#endif /* ! GRUB_TPM2_TCG2_HEADER */ diff --git a/grub-core/lib/tss2/tpm2_cmd.c b/grub-core/lib/tss2/tpm2_cmd.c new file mode 100644 index 000000000..cd0c6fd31 --- /dev/null +++ b/grub-core/lib/tss2/tpm2_cmd.c @@ -0,0 +1,1043 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 + +static TPM_RC_t +tpm2_submit_command_real (const TPMI_ST_COMMAND_TAG_t tag, + const TPM_CC_t commandCode, + TPM_RC_t *responseCode, + const struct grub_tpm2_buffer *in, + struct grub_tpm2_buffer *out) +{ + grub_err_t err; + struct grub_tpm2_buffer buf; + TPMI_ST_COMMAND_TAG_t tag_out; + grub_uint32_t command_size; + grub_size_t max_output_size; + + /* Marshal */ + grub_tpm2_buffer_init (&buf); + grub_tpm2_buffer_pack_u16 (&buf, tag); + grub_tpm2_buffer_pack_u32 (&buf, 0); + grub_tpm2_buffer_pack_u32 (&buf, commandCode); + grub_tpm2_buffer_pack (&buf, in->data, in->size); + + if (buf.error != 0) + return TPM_RC_FAILURE; + + /* Convert the command size to big endian to fill the u32 buffer below 'tag' */ + command_size = grub_cpu_to_be32 (buf.size); + grub_memcpy (&buf.data[sizeof (grub_uint16_t)], &command_size, sizeof (command_size)); + + /* Stay within output block limits */ + err = grub_tcg2_get_max_output_size (&max_output_size); + if (err != GRUB_ERR_NONE || max_output_size > out->cap) + max_output_size = out->cap - 1; + + /* Submit */ + err = grub_tcg2_submit_command (buf.size, buf.data, max_output_size, out->data); + if (err != GRUB_ERR_NONE) + return TPM_RC_FAILURE; + + /* Unmarshal */ + out->size = sizeof (grub_uint16_t) + sizeof (grub_uint32_t) + sizeof (grub_uint32_t); + grub_tpm2_buffer_unpack_u16 (out, &tag_out); + grub_tpm2_buffer_unpack_u32 (out, &command_size); + grub_tpm2_buffer_unpack_u32 (out, responseCode); + out->size = command_size; + if (out->error != 0) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} + +static TPM_RC_t +tpm2_submit_command (const TPMI_ST_COMMAND_TAG_t tag, + const TPM_CC_t commandCode, + TPM_RC_t *responseCode, + const struct grub_tpm2_buffer *in, + struct grub_tpm2_buffer *out) +{ + TPM_RC_t err; + int retry_cnt = 0; + + /* Catch TPM_RC_RETRY and send the command again */ + do { + err = tpm2_submit_command_real (tag, commandCode, responseCode, in, out); + if (*responseCode != TPM_RC_RETRY) + break; + + retry_cnt++; + } while (retry_cnt < 3); + + return err; +} + +TPM_RC_t +grub_tpm2_createprimary (const TPMI_RH_HIERARCHY_t primaryHandle, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_SENSITIVE_CREATE_t *inSensitive, + const TPM2B_PUBLIC_t *inPublic, + const TPM2B_DATA_t *outsideInfo, + const TPML_PCR_SELECTION_t *creationPCR, + TPM_HANDLE_t *objectHandle, + TPM2B_PUBLIC_t *outPublic, + TPM2B_CREATION_DATA_t *creationData, + TPM2B_DIGEST_t *creationHash, + TPMT_TK_CREATION_t *creationTicket, + TPM2B_NAME_t *name, + TPMS_AUTH_RESPONSE_t *authResponse) +{ + TPM_RC_t rc; + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + TPM_HANDLE_t objectHandleTmp; + TPM2B_PUBLIC_t outPublicTmp; + TPM2B_CREATION_DATA_t creationDataTmp; + TPM2B_DIGEST_t creationHashTmp; + TPMT_TK_CREATION_t creationTicketTmp; + TPM2B_NAME_t nameTmp; + TPMS_AUTH_RESPONSE_t authResponseTmp; + TPMI_ST_COMMAND_TAG_t tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; + TPM_RC_t responseCode; + grub_uint32_t parameterSize; + + if (inSensitive == NULL || inPublic == NULL || outsideInfo == NULL || + creationPCR == NULL) + return TPM_RC_VALUE; + + if (objectHandle == NULL) + objectHandle = &objectHandleTmp; + if (outPublic == NULL) + outPublic = &outPublicTmp; + if (creationData == NULL) + creationData = &creationDataTmp; + if (creationHash == NULL) + creationHash = &creationHashTmp; + if (creationTicket == NULL) + creationTicket = &creationTicketTmp; + if (name == NULL) + name = &nameTmp; + if (authResponse == NULL) + authResponse = &authResponseTmp; + + grub_memset (outPublic, 0, sizeof (*outPublic)); + grub_memset (creationData, 0, sizeof (*creationData)); + grub_memset (creationHash, 0, sizeof (*creationHash)); + grub_memset (creationTicket, 0, sizeof (*creationTicket)); + grub_memset (name, 0, sizeof (*name)); + grub_memset (authResponse, 0, sizeof (*authResponse)); + + /* Marshal */ + grub_tpm2_buffer_init (&in); + grub_tpm2_buffer_pack_u32 (&in, primaryHandle); + if (authCommand != NULL) + grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); + grub_Tss2_MU_TPM2B_SENSITIVE_CREATE_Marshal (&in, inSensitive); + grub_Tss2_MU_TPM2B_PUBLIC_Marshal (&in, inPublic); + grub_Tss2_MU_TPM2B_Marshal (&in, outsideInfo->size, outsideInfo->buffer); + grub_Tss2_MU_TPML_PCR_SELECTION_Marshal (&in, creationPCR); + if (in.error != 0) + 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; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal */ + grub_tpm2_buffer_unpack_u32 (&out, objectHandle); + if (tag == TPM_ST_SESSIONS) + grub_tpm2_buffer_unpack_u32 (&out, ¶meterSize); + grub_Tss2_MU_TPM2B_PUBLIC_Unmarshal (&out, outPublic); + grub_Tss2_MU_TPM2B_CREATION_DATA_Unmarshal (&out, creationData); + grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (&out, creationHash); + grub_Tss2_MU_TPMT_TK_CREATION_Unmarshal (&out, creationTicket); + grub_Tss2_MU_TPM2B_NAME_Unmarshal (&out, name); + if (tag == TPM_ST_SESSIONS) + grub_Tss2_MU_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); + if (out.error != 0) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} + +TPM_RC_t +grub_tpm2_startauthsession (const TPMI_DH_OBJECT_t tpmKey, + const TPMI_DH_ENTITY_t bind, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_NONCE_t *nonceCaller, + const TPM2B_ENCRYPTED_SECRET_t *encryptedSalt, + const TPM_SE_t sessionType, + const TPMT_SYM_DEF_t *symmetric, + const TPMI_ALG_HASH_t authHash, + TPMI_SH_AUTH_SESSION_t *sessionHandle, + TPM2B_NONCE_t *nonceTpm, + TPMS_AUTH_RESPONSE_t *authResponse) +{ + TPM_RC_t rc; + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + TPMI_SH_AUTH_SESSION_t sessionHandleTmp; + TPM2B_NONCE_t nonceTpmTmp; + TPMS_AUTH_RESPONSE_t authResponseTmp; + TPMI_ST_COMMAND_TAG_t tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; + TPM_RC_t responseCode; + grub_uint32_t param_size; + + if (nonceCaller == NULL || symmetric == NULL) + return TPM_RC_VALUE; + + if (tpmKey == TPM_RH_NULL && + (encryptedSalt && encryptedSalt->size != 0)) + return TPM_RC_VALUE; + + if (sessionHandle == NULL) + sessionHandle = &sessionHandleTmp; + if (nonceTpm == NULL) + nonceTpm = &nonceTpmTmp; + if (authResponse == NULL) + authResponse = &authResponseTmp; + + grub_memset (sessionHandle, 0, sizeof (*sessionHandle)); + grub_memset (nonceTpm, 0, sizeof (*nonceTpm)); + grub_memset (authResponse, 0, sizeof (*authResponse)); + + /* Marshal */ + grub_tpm2_buffer_init (&in); + grub_tpm2_buffer_pack_u32 (&in, tpmKey); + grub_tpm2_buffer_pack_u32 (&in, bind); + if (tag == TPM_ST_SESSIONS) + grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); + grub_Tss2_MU_TPM2B_Marshal (&in, nonceCaller->size, nonceCaller->buffer); + if (encryptedSalt != NULL) + grub_Tss2_MU_TPM2B_Marshal (&in, encryptedSalt->size, encryptedSalt->secret); + else + grub_tpm2_buffer_pack_u16 (&in, 0); + grub_tpm2_buffer_pack_u8 (&in, sessionType); + grub_Tss2_MU_TPMT_SYM_DEF_Marshal (&in, symmetric); + grub_tpm2_buffer_pack_u16 (&in, authHash); + if (in.error != 0) + 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) + return rc; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal */ + grub_tpm2_buffer_unpack_u32 (&out, sessionHandle); + if (tag == TPM_ST_SESSIONS) + grub_tpm2_buffer_unpack_u32 (&out, ¶m_size); + grub_Tss2_MU_TPM2B_NONCE_Unmarshal (&out, nonceTpm); + if (tag == TPM_ST_SESSIONS) + grub_Tss2_MU_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); + if (out.error != 0) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} + +TPM_RC_t +grub_tpm2_policypcr (const TPMI_SH_POLICY_t policySessions, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_DIGEST_t *pcrDigest, + const TPML_PCR_SELECTION_t *pcrs, + TPMS_AUTH_RESPONSE_t *authResponse) +{ + TPM_RC_t rc; + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + TPMS_AUTH_RESPONSE_t authResponseTmp; + TPMI_ST_COMMAND_TAG_t tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; + TPM_RC_t responseCode; + grub_uint32_t param_size; + + if (pcrs == NULL) + return TPM_RC_VALUE; + + if (authResponse == NULL) + authResponse = &authResponseTmp; + + grub_memset (authResponse, 0, sizeof (*authResponse)); + + /* Marshal */ + grub_tpm2_buffer_init (&in); + grub_tpm2_buffer_pack_u32 (&in, policySessions); + if (tag == TPM_ST_SESSIONS) + grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); + if (pcrDigest != NULL) + grub_Tss2_MU_TPM2B_Marshal (&in, pcrDigest->size, pcrDigest->buffer); + else + grub_tpm2_buffer_pack_u16 (&in, 0); + grub_Tss2_MU_TPML_PCR_SELECTION_Marshal (&in, pcrs); + if (in.error != 0) + 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; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal*/ + if (tag == TPM_ST_SESSIONS) + grub_tpm2_buffer_unpack_u32 (&out, ¶m_size); + if (tag == TPM_ST_SESSIONS) + grub_Tss2_MU_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); + if (out.error != 0) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} + +TPM_RC_t +grub_tpm2_readpublic (const TPMI_DH_OBJECT_t objectHandle, + const TPMS_AUTH_COMMAND_t *authCommand, + TPM2B_PUBLIC_t *outPublic) +{ + TPM_RC_t rc; + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + TPMI_ST_COMMAND_TAG_t tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; + TPM_RC_t responseCode; + grub_uint32_t parameterSize; + + /* Marshal */ + grub_tpm2_buffer_init (&in); + grub_tpm2_buffer_pack_u32 (&in, objectHandle); + if (in.error != 0) + 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; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal */ + if (tag == TPM_ST_SESSIONS) + grub_tpm2_buffer_unpack_u32 (&out, ¶meterSize); + grub_Tss2_MU_TPM2B_PUBLIC_Unmarshal (&out, outPublic); + if (out.error != 0) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} + +TPM_RC_t +grub_tpm2_load (const TPMI_DH_OBJECT_t parent_handle, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_PRIVATE_t *inPrivate, + const TPM2B_PUBLIC_t *inPublic, + TPM_HANDLE_t *objectHandle, + TPM2B_NAME_t *name, + TPMS_AUTH_RESPONSE_t *authResponse) +{ + TPM_RC_t rc; + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + TPM_HANDLE_t objectHandleTmp; + TPM2B_NAME_t nameTmp; + TPMS_AUTH_RESPONSE_t authResponseTmp; + TPMI_ST_COMMAND_TAG_t tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; + TPM_RC_t responseCode; + grub_uint32_t param_size; + + if (inPrivate == NULL || inPublic == NULL) + return TPM_RC_VALUE; + + if (objectHandle == NULL) + objectHandle = &objectHandleTmp; + if (name == NULL) + name = &nameTmp; + if (authResponse == NULL) + authResponse = &authResponseTmp; + + grub_memset (objectHandle, 0, sizeof (*objectHandle)); + grub_memset (name, 0, sizeof (*name)); + grub_memset (authResponse, 0, sizeof (*authResponse)); + + /* Marshal */ + grub_tpm2_buffer_init (&in); + grub_tpm2_buffer_pack_u32 (&in, parent_handle); + if (authCommand) + grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); + grub_Tss2_MU_TPM2B_Marshal (&in, inPrivate->size, inPrivate->buffer); + grub_Tss2_MU_TPM2B_PUBLIC_Marshal (&in, inPublic); + if (in.error != 0) + 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; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal */ + grub_tpm2_buffer_unpack_u32 (&out, objectHandle); + if (tag == TPM_ST_SESSIONS) + grub_tpm2_buffer_unpack_u32 (&out, ¶m_size); + grub_Tss2_MU_TPM2B_NAME_Unmarshal (&out, name); + if (tag == TPM_ST_SESSIONS) + grub_Tss2_MU_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); + if (out.error != 0) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} + +TPM_RC_t +grub_tpm2_loadexternal (const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_SENSITIVE_t *inPrivate, + const TPM2B_PUBLIC_t *inPublic, + const TPMI_RH_HIERARCHY_t hierarchy, + TPM_HANDLE_t *objectHandle, + TPM2B_NAME_t *name, + TPMS_AUTH_RESPONSE_t *authResponse) +{ + TPM_RC_t rc; + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + TPM_HANDLE_t objectHandleTmp; + TPM2B_NAME_t nameTmp; + TPMS_AUTH_RESPONSE_t authResponseTmp; + TPMI_ST_COMMAND_TAG_t tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; + TPM_RC_t responseCode; + grub_uint32_t param_size; + + if (inPublic == NULL) + return TPM_RC_VALUE; + + if (objectHandle == NULL) + objectHandle = &objectHandleTmp; + if (name == NULL) + name = &nameTmp; + if (authResponse == NULL) + authResponse = &authResponseTmp; + + grub_memset (objectHandle, 0, sizeof (*objectHandle)); + grub_memset (name, 0, sizeof (*name)); + grub_memset (authResponse, 0, sizeof (*authResponse)); + + /* Marshal */ + grub_tpm2_buffer_init (&in); + if (authCommand) + grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); + if (inPrivate) + grub_Tss2_MU_TPM2B_SENSITIVE_Marshal (&in, inPrivate); + else + grub_tpm2_buffer_pack_u16 (&in, 0); + grub_Tss2_MU_TPM2B_PUBLIC_Marshal (&in, inPublic); + grub_tpm2_buffer_pack_u32 (&in, hierarchy); + if (in.error != 0) + 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; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal */ + grub_tpm2_buffer_unpack_u32 (&out, objectHandle); + if (tag == TPM_ST_SESSIONS) + grub_tpm2_buffer_unpack_u32 (&out, ¶m_size); + grub_Tss2_MU_TPM2B_NAME_Unmarshal (&out, name); + if (tag == TPM_ST_SESSIONS) + grub_Tss2_MU_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); + if (out.error != 0) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} + +TPM_RC_t +grub_tpm2_unseal (const TPMI_DH_OBJECT_t itemHandle, + const TPMS_AUTH_COMMAND_t *authCommand, + TPM2B_SENSITIVE_DATA_t *outData, + TPMS_AUTH_RESPONSE_t *authResponse) +{ + TPM_RC_t rc; + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + TPM2B_SENSITIVE_DATA_t outDataTmp; + TPMS_AUTH_RESPONSE_t authResponseTmp; + TPMI_ST_COMMAND_TAG_t tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; + TPM_RC_t responseCode; + grub_uint32_t param_size; + + if (outData == NULL) + outData = &outDataTmp; + if (authResponse == NULL) + authResponse = &authResponseTmp; + + grub_memset (outData, 0, sizeof (*outData)); + grub_memset (authResponse, 0, sizeof (*authResponse)); + + /* Marshal */ + grub_tpm2_buffer_init (&in); + grub_tpm2_buffer_pack_u32 (&in, itemHandle); + if (authCommand != NULL) + grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); + if (in.error != 0) + 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; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal */ + if (tag == TPM_ST_SESSIONS) + grub_tpm2_buffer_unpack_u32 (&out, ¶m_size); + grub_Tss2_MU_TPM2B_SENSITIVE_DATA_Unmarshal (&out, outData); + if (tag == TPM_ST_SESSIONS) + grub_Tss2_MU_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); + if (out.error != 0) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} + +TPM_RC_t +grub_tpm2_flushcontext (const TPMI_DH_CONTEXT_t handle) +{ + TPM_RC_t rc; + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + TPM_RC_t responseCode; + + /* Marshal */ + grub_tpm2_buffer_init (&in); + grub_tpm2_buffer_pack_u32 (&in, handle); + if (in.error != 0) + 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; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal */ + if (out.error != 0) + 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, + grub_uint32_t *pcrUpdateCounter, + TPML_PCR_SELECTION_t *pcrSelectionOut, + TPML_DIGEST_t *pcrValues, + TPMS_AUTH_RESPONSE_t *authResponse) +{ + TPM_RC_t rc; + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + grub_uint32_t pcrUpdateCounterTmp; + TPML_PCR_SELECTION_t pcrSelectionOutTmp; + TPML_DIGEST_t pcrValuesTmp; + TPMS_AUTH_RESPONSE_t authResponseTmp; + TPMI_ST_COMMAND_TAG_t tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; + TPM_RC_t responseCode; + grub_uint32_t parameterSize; + + if (pcrSelectionIn == NULL) + return TPM_RC_VALUE; + + if (pcrUpdateCounter == NULL) + pcrUpdateCounter = &pcrUpdateCounterTmp; + if (pcrSelectionOut == NULL) + pcrSelectionOut = &pcrSelectionOutTmp; + if (pcrValues == NULL) + pcrValues = &pcrValuesTmp; + if (authResponse == NULL) + authResponse = &authResponseTmp; + + /* Marshal */ + grub_tpm2_buffer_init (&in); + if (authCommand != NULL) + grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); + grub_Tss2_MU_TPML_PCR_SELECTION_Marshal (&in, pcrSelectionIn); + if (in.error != 0) + 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; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal */ + if (tag == TPM_ST_SESSIONS) + grub_tpm2_buffer_unpack_u32 (&out, ¶meterSize); + grub_tpm2_buffer_unpack_u32 (&out, pcrUpdateCounter); + grub_Tss2_MU_TPML_PCR_SELECTION_Unmarshal (&out, pcrSelectionOut); + grub_Tss2_MU_TPML_DIGEST_Unmarshal (&out, pcrValues); + if (tag == TPM_ST_SESSIONS) + grub_Tss2_MU_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); + if (out.error != 0) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} + +TPM_RC_t +grub_tpm2_policygetdigest (const TPMI_SH_POLICY_t policySession, + const TPMS_AUTH_COMMAND_t *authCommand, + TPM2B_DIGEST_t *policyDigest, + TPMS_AUTH_RESPONSE_t *authResponse) +{ + TPM_RC_t rc; + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + TPMS_AUTH_RESPONSE_t authResponseTmp; + TPM2B_DIGEST_t policyDigestTmp; + TPMI_ST_COMMAND_TAG_t tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; + TPM_RC_t responseCode; + grub_uint32_t parameterSize; + + if (authResponse == NULL) + authResponse = &authResponseTmp; + if (policyDigest == NULL) + policyDigest = &policyDigestTmp; + + grub_memset (authResponse, 0, sizeof (*authResponse)); + grub_memset (policyDigest, 0, sizeof (*policyDigest)); + + /* Submit */ + grub_tpm2_buffer_init (&in); + grub_tpm2_buffer_pack_u32 (&in, policySession); + if (tag == TPM_ST_SESSIONS) + grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); + if (in.error != 0) + 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; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal */ + if (tag == TPM_ST_SESSIONS) + grub_tpm2_buffer_unpack_u32 (&out, ¶meterSize); + grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (&out, policyDigest); + if (tag == TPM_ST_SESSIONS) + grub_Tss2_MU_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); + if (out.error != 0) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} + +TPM_RC_t +grub_tpm2_create (const TPMI_DH_OBJECT_t parentHandle, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_SENSITIVE_CREATE_t *inSensitive, + const TPM2B_PUBLIC_t *inPublic, + const TPM2B_DATA_t *outsideInfo, + const TPML_PCR_SELECTION_t *creationPCR, + TPM2B_PRIVATE_t *outPrivate, + TPM2B_PUBLIC_t *outPublic, + TPM2B_CREATION_DATA_t *creationData, + TPM2B_DIGEST_t *creationHash, + TPMT_TK_CREATION_t *creationTicket, + TPMS_AUTH_RESPONSE_t *authResponse) +{ + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + TPM2B_PUBLIC_t outPublicTmp; + TPM2B_PRIVATE_t outPrivateTmp; + TPM2B_CREATION_DATA_t creationDataTmp; + TPM2B_DIGEST_t creationHashTmp; + TPMT_TK_CREATION_t creationTicketTmp; + TPMS_AUTH_RESPONSE_t authResponseTmp; + TPMI_ST_COMMAND_TAG_t tag = authCommand ? TPM_ST_SESSIONS:TPM_ST_NO_SESSIONS; + TPM_RC_t responseCode; + TPM_RC_t rc; + grub_uint32_t parameterSize; + + if (inSensitive == NULL || inPublic == NULL || outsideInfo == NULL || + creationPCR == NULL) + return TPM_RC_VALUE; + + if (outPrivate == NULL) + outPrivate = &outPrivateTmp; + if (outPublic == NULL) + outPublic = &outPublicTmp; + if (creationData == NULL) + creationData = &creationDataTmp; + if (creationHash == NULL) + creationHash = &creationHashTmp; + if (creationTicket == NULL) + creationTicket = &creationTicketTmp; + if (authResponse == NULL) + authResponse = &authResponseTmp; + + grub_memset (outPrivate, 0, sizeof (*outPrivate)); + grub_memset (outPublic, 0, sizeof (*outPublic)); + grub_memset (creationData, 0, sizeof (*creationData)); + grub_memset (creationHash, 0, sizeof (*creationHash)); + grub_memset (creationTicket, 0, sizeof (*creationTicket)); + grub_memset (authResponse, 0, sizeof (*authResponse)); + + /* Marshal */ + grub_tpm2_buffer_init (&in); + grub_tpm2_buffer_pack_u32 (&in, parentHandle); + if (authCommand != NULL) + grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); + grub_Tss2_MU_TPM2B_SENSITIVE_CREATE_Marshal (&in, inSensitive); + grub_Tss2_MU_TPM2B_PUBLIC_Marshal (&in, inPublic); + grub_Tss2_MU_TPM2B_Marshal (&in, outsideInfo->size, outsideInfo->buffer); + grub_Tss2_MU_TPML_PCR_SELECTION_Marshal (&in, creationPCR); + if (in.error != 0) + 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; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal */ + if (tag == TPM_ST_SESSIONS) + grub_tpm2_buffer_unpack_u32 (&out, ¶meterSize); + grub_Tss2_MU_TPM2B_PRIVATE_Unmarshal (&out, outPrivate); + grub_Tss2_MU_TPM2B_PUBLIC_Unmarshal (&out, outPublic); + grub_Tss2_MU_TPM2B_CREATION_DATA_Unmarshal (&out, creationData); + grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (&out, creationHash); + grub_Tss2_MU_TPMT_TK_CREATION_Unmarshal (&out, creationTicket); + if (tag == TPM_ST_SESSIONS) + grub_Tss2_MU_TPMS_AUTH_RESPONSE_Unmarshal(&out, authResponse); + if (out.error != 0) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} + +TPM_RC_t +grub_tpm2_evictcontrol (const TPMI_RH_PROVISION_t auth, + const TPMI_DH_OBJECT_t objectHandle, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPMI_DH_PERSISTENT_t persistentHandle, + TPMS_AUTH_RESPONSE_t *authResponse) +{ + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + TPMS_AUTH_RESPONSE_t authResponseTmp; + TPMI_ST_COMMAND_TAG_t tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; + TPM_RC_t responseCode; + TPM_RC_t rc; + grub_uint32_t parameterSize; + + if (authResponse == NULL) + authResponse = &authResponseTmp; + + grub_memset (authResponse, 0, sizeof (*authResponse)); + + /* Marshal */ + grub_tpm2_buffer_init (&in); + grub_tpm2_buffer_pack_u32 (&in, auth); + grub_tpm2_buffer_pack_u32 (&in, objectHandle); + if (authCommand != NULL) + grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); + grub_tpm2_buffer_pack_u32 (&in, persistentHandle); + if (in.error != 0) + 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; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal */ + if (tag == TPM_ST_SESSIONS) + { + grub_tpm2_buffer_unpack_u32 (&out, ¶meterSize); + grub_Tss2_MU_TPMS_AUTH_RESPONSE_Unmarshal(&out, authResponse); + } + if (out.error != 0) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} + +TPM_RC_t +grub_tpm2_hash (const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_MAX_BUFFER_t *data, + const TPMI_ALG_HASH_t hashAlg, + const TPMI_RH_HIERARCHY_t hierarchy, + TPM2B_DIGEST_t *outHash, + TPMT_TK_HASHCHECK_t *validation, + TPMS_AUTH_RESPONSE_t *authResponse) +{ + TPM_RC_t rc; + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + TPMS_AUTH_RESPONSE_t authResponseTmp; + TPM2B_DIGEST_t outHashTmp; + TPMT_TK_HASHCHECK_t validationTmp; + TPMI_ST_COMMAND_TAG_t tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; + TPM_RC_t responseCode; + grub_uint32_t param_size; + + if (hashAlg == TPM_ALG_NULL) + return TPM_RC_VALUE; + + if (outHash == NULL) + outHash = &outHashTmp; + if (validation == NULL) + validation = &validationTmp; + if (authResponse == NULL) + authResponse = &authResponseTmp; + + grub_memset (outHash, 0, sizeof (*outHash)); + grub_memset (validation, 0, sizeof (*validation)); + grub_memset (authResponse, 0, sizeof (*authResponse)); + + /* Marshal */ + grub_tpm2_buffer_init (&in); + if (authCommand != NULL) + grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); + if (data != NULL) + grub_Tss2_MU_TPM2B_Marshal (&in, data->size, data->buffer); + else + grub_tpm2_buffer_pack_u16 (&in, 0); + grub_tpm2_buffer_pack_u16 (&in, hashAlg); + grub_tpm2_buffer_pack_u32 (&in, hierarchy); + if (in.error != 0) + 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; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal */ + if (tag == TPM_ST_SESSIONS) + grub_tpm2_buffer_unpack_u32 (&out, ¶m_size); + grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (&out, outHash); + grub_Tss2_MU_TPMT_TK_HASHCHECK_Unmarshal (&out, validation); + if (tag == TPM_ST_SESSIONS) + grub_Tss2_MU_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); + if (out.error != 0) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} + +TPM_RC_t +grub_tpm2_verifysignature (const TPMI_DH_OBJECT_t keyHandle, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_DIGEST_t *digest, + const TPMT_SIGNATURE_t *signature, + TPMT_TK_VERIFIED_t *validation, + TPMS_AUTH_RESPONSE_t *authResponse) +{ + TPM_RC_t rc; + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + TPMS_AUTH_RESPONSE_t authResponseTmp; + TPMI_ST_COMMAND_TAG_t tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; + TPMT_TK_VERIFIED_t validationTmp; + TPM_RC_t responseCode; + grub_uint32_t param_size; + + if (digest == NULL || signature == NULL) + return TPM_RC_VALUE; + + if (validation == NULL) + validation = &validationTmp; + if (authResponse == NULL) + authResponse = &authResponseTmp; + + grub_memset (validation, 0, sizeof (*validation)); + grub_memset (authResponse, 0, sizeof (*authResponse)); + + /* Marshal */ + grub_tpm2_buffer_init (&in); + if (authCommand != NULL) + grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); + grub_tpm2_buffer_pack_u32 (&in, keyHandle); + grub_Tss2_MU_TPM2B_Marshal (&in, digest->size, digest->buffer); + grub_Tss2_MU_TPMT_SIGNATURE_Marshal (&in, signature); + if (in.error != 0) + 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; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal */ + if (tag == TPM_ST_SESSIONS) + grub_tpm2_buffer_unpack_u32 (&out, ¶m_size); + grub_Tss2_MU_TPMT_TK_VERIFIED_Unmarshal (&out, validation); + if (tag == TPM_ST_SESSIONS) + grub_Tss2_MU_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); + if (out.error != 0) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} + +TPM_RC_t +grub_tpm2_policyauthorize (const TPMI_SH_POLICY_t policySession, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_DIGEST_t *approvedPolicy, + const TPM2B_NONCE_t *policyRef, + const TPM2B_NAME_t *keySign, + const TPMT_TK_VERIFIED_t *checkTicket, + TPMS_AUTH_RESPONSE_t *authResponse) +{ + TPM_RC_t rc; + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + TPMS_AUTH_RESPONSE_t authResponseTmp; + TPMI_ST_COMMAND_TAG_t tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; + TPM_RC_t responseCode; + grub_uint32_t param_size; + + if (approvedPolicy == NULL || keySign == NULL || checkTicket == NULL) + return TPM_RC_VALUE; + + if (authResponse == NULL) + authResponse = &authResponseTmp; + + grub_memset (authResponse, 0, sizeof (*authResponse)); + + /* Marshal */ + grub_tpm2_buffer_init (&in); + grub_tpm2_buffer_pack_u32 (&in, policySession); + if (authCommand != NULL) + grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); + grub_Tss2_MU_TPM2B_Marshal (&in, approvedPolicy->size, approvedPolicy->buffer); + if (policyRef != NULL) + grub_Tss2_MU_TPM2B_Marshal (&in, policyRef->size, policyRef->buffer); + else + grub_tpm2_buffer_pack_u16 (&in, 0); + grub_Tss2_MU_TPM2B_Marshal (&in, keySign->size, keySign->name); + grub_Tss2_MU_TPMT_TK_VERIFIED_Marshal (&in, checkTicket); + if (in.error != 0) + 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; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal */ + if (tag == TPM_ST_SESSIONS) + { + grub_tpm2_buffer_unpack_u32 (&out, ¶m_size); + grub_Tss2_MU_TPMS_AUTH_RESPONSE_Unmarshal (&out, authResponse); + } + if (out.error != 0) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} + +TPM_RC_t +grub_tpm2_testparms (const TPMT_PUBLIC_PARMS_t *parms, + const TPMS_AUTH_COMMAND_t *authCommand) +{ + TPM_RC_t rc; + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + TPMI_ST_COMMAND_TAG_t tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; + TPM_RC_t responseCode; + + if (parms == NULL) + return TPM_RC_VALUE; + + /* Marshal */ + grub_tpm2_buffer_init (&in); + grub_Tss2_MU_TPMT_PUBLIC_PARMS_Marshal (&in, parms); + if (in.error != 0) + 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) + return rc; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal */ + if (out.error != 0) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} diff --git a/grub-core/lib/tss2/tpm2_cmd.h b/grub-core/lib/tss2/tpm2_cmd.h new file mode 100644 index 000000000..d313cba00 --- /dev/null +++ b/grub-core/lib/tss2/tpm2_cmd.h @@ -0,0 +1,157 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 GRUB_TPM2_COMMANDS_HEADER +#define GRUB_TPM2_COMMANDS_HEADER 1 + +#include + +extern TPM_RC_t +grub_tpm2_createprimary (const TPMI_RH_HIERARCHY_t primaryHandle, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_SENSITIVE_CREATE_t *inSensitive, + const TPM2B_PUBLIC_t *inPublic, + const TPM2B_DATA_t *outsideInfo, + const TPML_PCR_SELECTION_t *creationPCR, + TPM_HANDLE_t *objectHandle, + TPM2B_PUBLIC_t *outPublic, + TPM2B_CREATION_DATA_t *creationData, + TPM2B_DIGEST_t *creationHash, + TPMT_TK_CREATION_t *creationTicket, + TPM2B_NAME_t *name, + TPMS_AUTH_RESPONSE_t *authResponse); + +extern TPM_RC_t +grub_tpm2_startauthsession (const TPMI_DH_OBJECT_t tpmKey, + const TPMI_DH_ENTITY_t bind, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_NONCE_t *nonceCaller, + const TPM2B_ENCRYPTED_SECRET_t *encryptedSalt, + const TPM_SE_t sessionType, + const TPMT_SYM_DEF_t *symmetric, + const TPMI_ALG_HASH_t authHash, + TPMI_SH_AUTH_SESSION_t *sessionHandle, + TPM2B_NONCE_t *nonceTpm, + TPMS_AUTH_RESPONSE_t *authResponse); + +extern TPM_RC_t +grub_tpm2_policypcr (const TPMI_SH_POLICY_t policySession, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_DIGEST_t *pcrDigest, + const TPML_PCR_SELECTION_t *pcrs, + TPMS_AUTH_RESPONSE_t *authResponse); + +extern TPM_RC_t +grub_tpm2_readpublic (const TPMI_DH_OBJECT_t objectHandle, + const TPMS_AUTH_COMMAND_t *authCommand, + TPM2B_PUBLIC_t *outPublic); + +extern TPM_RC_t +grub_tpm2_load (const TPMI_DH_OBJECT_t parent_handle, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_PRIVATE_t *inPrivate, + const TPM2B_PUBLIC_t *inPublic, + TPM_HANDLE_t *objectHandle, + TPM2B_NAME_t *name, + TPMS_AUTH_RESPONSE_t *authResponse); + +extern TPM_RC_t +grub_tpm2_loadexternal (const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_SENSITIVE_t *inPrivate, + const TPM2B_PUBLIC_t *inPublic, + const TPMI_RH_HIERARCHY_t hierarchy, + TPM_HANDLE_t *objectHandle, + TPM2B_NAME_t *name, + TPMS_AUTH_RESPONSE_t *authResponse); + +extern TPM_RC_t +grub_tpm2_unseal (const TPMI_DH_OBJECT_t item_handle, + const TPMS_AUTH_COMMAND_t *authCommand, + TPM2B_SENSITIVE_DATA_t *outData, + TPMS_AUTH_RESPONSE_t *authResponse); + +extern TPM_RC_t +grub_tpm2_flushcontext (const TPMI_DH_CONTEXT_t handle); + +extern TPM_RC_t +grub_tpm2_pcr_read (const TPMS_AUTH_COMMAND_t *authCommand, + const TPML_PCR_SELECTION_t *pcrSelectionIn, + grub_uint32_t *pcrUpdateCounter, + TPML_PCR_SELECTION_t *pcrSelectionOut, + TPML_DIGEST_t *pcrValues, + TPMS_AUTH_RESPONSE_t *authResponse); + +extern TPM_RC_t +grub_tpm2_policygetdigest (const TPMI_SH_POLICY_t policySession, + const TPMS_AUTH_COMMAND_t *authCommand, + TPM2B_DIGEST_t *policyDigest, + TPMS_AUTH_RESPONSE_t *authResponse); + +extern TPM_RC_t +grub_tpm2_create (const TPMI_DH_OBJECT_t parentHandle, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_SENSITIVE_CREATE_t *inSensitive, + const TPM2B_PUBLIC_t *inPublic, + const TPM2B_DATA_t *outsideInfo, + const TPML_PCR_SELECTION_t *creationPCR, + TPM2B_PRIVATE_t *outPrivate, + TPM2B_PUBLIC_t *outPublic, + TPM2B_CREATION_DATA_t *creationData, + TPM2B_DIGEST_t *creationHash, + TPMT_TK_CREATION_t *creationTicket, + TPMS_AUTH_RESPONSE_t *authResponse); + +extern TPM_RC_t +grub_tpm2_evictcontrol (const TPMI_RH_PROVISION_t auth, + const TPMI_DH_OBJECT_t objectHandle, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPMI_DH_PERSISTENT_t persistentHandle, + TPMS_AUTH_RESPONSE_t *authResponse); + +extern TPM_RC_t +grub_tpm2_hash (const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_MAX_BUFFER_t *data, + const TPMI_ALG_HASH_t hashAlg, + const TPMI_RH_HIERARCHY_t hierarchy, + TPM2B_DIGEST_t *outHash, + TPMT_TK_HASHCHECK_t *validation, + TPMS_AUTH_RESPONSE_t *authResponse); + +extern TPM_RC_t +grub_tpm2_verifysignature (const TPMI_DH_OBJECT_t keyHandle, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_DIGEST_t *digest, + const TPMT_SIGNATURE_t *signature, + TPMT_TK_VERIFIED_t *validation, + TPMS_AUTH_RESPONSE_t *authResponse); + +extern TPM_RC_t +grub_tpm2_policyauthorize (const TPMI_SH_POLICY_t policySession, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_DIGEST_t *approvedPolicy, + const TPM2B_NONCE_t *policyRef, + const TPM2B_NAME_t *keySign, + const TPMT_TK_VERIFIED_t *checkTicket, + TPMS_AUTH_RESPONSE_t *authResponse); + +extern TPM_RC_t +grub_tpm2_testparms (const TPMT_PUBLIC_PARMS_t *parms, + const TPMS_AUTH_COMMAND_t *authCommand); + +#endif /* ! GRUB_TPM2_COMMANDS_HEADER */ diff --git a/grub-core/lib/tss2/tss2.c b/grub-core/lib/tss2/tss2.c new file mode 100644 index 000000000..48251e9b4 --- /dev/null +++ b/grub-core/lib/tss2/tss2.c @@ -0,0 +1,21 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2024 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+"); From 48e230c317b3a65d262cc801633610496145e951 Mon Sep 17 00:00:00 2001 From: Hernan Gatta Date: Fri, 15 Nov 2024 15:34:51 +0800 Subject: [PATCH 232/402] key_protector: Add TPM2 Key Protector The TPM2 key protector is a module that enables the automatic retrieval of a fully-encrypted disk's unlocking key from a TPM 2.0. The theory of operation is such that the module accepts various arguments, most of which are optional and therefore possess reasonable defaults. One of these arguments is the keyfile/tpm2key parameter, which is mandatory. There are two supported key formats: 1. Raw Sealed Key (--keyfile) When sealing a key with TPM2_Create, the public portion of the sealed key is stored in TPM2B_PUBLIC, and the private portion is in TPM2B_PRIVATE. The raw sealed key glues the fully marshalled TPM2B_PUBLIC and TPM2B_PRIVATE into one file. 2. TPM 2.0 Key (--tpm2key) The following is the ASN.1 definition of TPM 2.0 Key File: TPMPolicy ::= SEQUENCE { CommandCode [0] EXPLICIT INTEGER CommandPolicy [1] EXPLICIT OCTET STRING } TPMAuthPolicy ::= SEQUENCE { Name [0] EXPLICIT UTF8STRING OPTIONAL Policy [1] EXPLICIT SEQUENCE OF TPMPolicy } TPMKey ::= SEQUENCE { type OBJECT IDENTIFIER emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL secret [2] EXPLICIT OCTET STRING OPTIONAL authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL description [4] EXPLICIT UTF8String OPTIONAL, rsaParent [5] EXPLICIT BOOLEAN OPTIONAL, parent INTEGER pubkey OCTET STRING privkey OCTET STRING } The TPM2 key protector only expects a "sealed" key in DER encoding, so "type" is always 2.23.133.10.1.5, "emptyAuth" is "TRUE", and "secret" is empty. "policy" and "authPolicy" are the possible policy command sequences to construct the policy digest to unseal the key. Similar to the raw sealed key, the public portion (TPM2B_PUBLIC) of the sealed key is stored in "pubkey", and the private portion (TPM2B_PRIVATE) is in "privkey". For more details: https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html This sealed key file is created via the grub-protect tool. The tool utilizes the TPM's sealing functionality to seal (i.e., encrypt) an unlocking key using a Storage Root Key (SRK) to the values of various Platform Configuration Registers (PCRs). These PCRs reflect the state of the system as it boots. If the values are as expected, the system may be considered trustworthy, at which point the TPM allows for a caller to utilize the private component of the SRK to unseal (i.e., decrypt) the sealed key file. The caller, in this case, is this key protector. The TPM2 key protector registers two commands: - tpm2_key_protector_init: Initializes the state of the TPM2 key protector for later usage, clearing any previous state, too, if any. - tpm2_key_protector_clear: Clears any state set by tpm2_key_protector_init. The way this is expected to be used requires the user to, either interactively or, normally, via a boot script, initialize/configure the key protector and then specify that it be used by the "cryptomount" command (modifications to this command are in a different patch). For instance, to unseal the raw sealed key file: tpm2_key_protector_init --keyfile=(hd0,gpt1)/efi/grub/sealed-1.key cryptomount -u -P tpm2 tpm2_key_protector_init --keyfile=(hd0,gpt1)/efi/grub/sealed-2.key --pcrs=7,11 cryptomount -u -P tpm2 Or, to unseal the TPM 2.0 Key file: tpm2_key_protector_init --tpm2key=(hd0,gpt1)/efi/grub/sealed-1.tpm cryptomount -u -P tpm2 tpm2_key_protector_init --tpm2key=(hd0,gpt1)/efi/grub/sealed-2.tpm --pcrs=7,11 cryptomount -u -P tpm2 If a user does not initialize the key protector and attempts to use it anyway, the protector returns an error. Before unsealing the key, the TPM2 key protector follows the "TPMPolicy" sequences to enforce the TPM policy commands to construct a valid policy digest to unseal the key. For the TPM 2.0 Key files, "authPolicy" may contain multiple "TPMPolicy" sequences, the TPM2 key protector iterates "authPolicy" to find a valid sequence to unseal key. If "authPolicy" is empty or all sequences in "authPolicy" fail, the protector tries the one from "policy". In case "policy" is also empty, the protector creates a "TPMPolicy" sequence based on the given PCR selection. For the raw sealed key, the TPM2 key protector treats the key file as a TPM 2.0 Key file without "authPolicy" and "policy", so the "TPMPolicy" sequence is always based on the PCR selection from the command parameters. This commit only supports one policy command: TPM2_PolicyPCR. The command set will be extended to support advanced features, such as authorized policy, in the later commits. Cc: James Bottomley Signed-off-by: Hernan Gatta Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Reviewed-by: Stefan Berger Tested-by: Stefan Berger --- grub-core/Makefile.core.def | 11 + grub-core/commands/tpm2_key_protector/args.c | 127 ++ .../commands/tpm2_key_protector/module.c | 1153 +++++++++++++++++ grub-core/commands/tpm2_key_protector/tpm2.h | 36 + .../commands/tpm2_key_protector/tpm2_args.h | 49 + .../commands/tpm2_key_protector/tpm2key.asn | 49 + .../commands/tpm2_key_protector/tpm2key.c | 499 +++++++ .../commands/tpm2_key_protector/tpm2key.h | 87 ++ .../tpm2_key_protector/tpm2key_asn1_tab.c | 63 + 9 files changed, 2074 insertions(+) create mode 100644 grub-core/commands/tpm2_key_protector/args.c create mode 100644 grub-core/commands/tpm2_key_protector/module.c create mode 100644 grub-core/commands/tpm2_key_protector/tpm2.h create mode 100644 grub-core/commands/tpm2_key_protector/tpm2_args.h create mode 100644 grub-core/commands/tpm2_key_protector/tpm2key.asn create mode 100644 grub-core/commands/tpm2_key_protector/tpm2key.c create mode 100644 grub-core/commands/tpm2_key_protector/tpm2key.h create mode 100644 grub-core/commands/tpm2_key_protector/tpm2key_asn1_tab.c diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 45b705a34..97ae4e49b 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -2578,6 +2578,17 @@ module = { cppflags = '-I$(srcdir)/lib/tss2'; }; +module = { + name = tpm2_key_protector; + common = commands/tpm2_key_protector/args.c; + common = commands/tpm2_key_protector/module.c; + common = commands/tpm2_key_protector/tpm2key.c; + common = commands/tpm2_key_protector/tpm2key_asn1_tab.c; + /* The plaform support of tpm2_key_protector depends on the tcg2 implementation in tss2. */ + enable = efi; + cppflags = '-I$(srcdir)/lib/tss2 -I$(srcdir)/lib/libtasn1-grub'; +}; + module = { name = tr; common = commands/tr.c; diff --git a/grub-core/commands/tpm2_key_protector/args.c b/grub-core/commands/tpm2_key_protector/args.c new file mode 100644 index 000000000..48c39de01 --- /dev/null +++ b/grub-core/commands/tpm2_key_protector/args.c @@ -0,0 +1,127 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 "tpm2_args.h" + +grub_err_t +grub_tpm2_protector_parse_pcrs (char *value, grub_uint8_t *pcrs, + grub_uint8_t *pcr_count) +{ + char *current_pcr = value; + char *next_pcr; + const char *pcr_end; + grub_uint64_t pcr; + grub_uint8_t i; + + if (grub_strlen (value) == 0) + return GRUB_ERR_BAD_ARGUMENT; + + *pcr_count = 0; + for (i = 0; i < TPM_MAX_PCRS; i++) + { + next_pcr = grub_strchr (current_pcr, ','); + if (next_pcr == current_pcr) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("empty entry in PCR list")); + if (next_pcr != NULL) + *next_pcr = '\0'; + + pcr = grub_strtoul (current_pcr, &pcr_end, 10); + if (*current_pcr == '\0' || *pcr_end != '\0') + return grub_error (GRUB_ERR_BAD_NUMBER, N_("entry '%s' in PCR list is not a number"), current_pcr); + + if (pcr > TPM_MAX_PCRS - 1) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("entry %llu in PCR list is too large to be a PCR number, PCR numbers range from 0 to %u"), (unsigned long long)pcr, TPM_MAX_PCRS - 1); + + pcrs[i] = (grub_uint8_t) pcr; + ++(*pcr_count); + + if (next_pcr == NULL) + break; + + current_pcr = next_pcr + 1; + if (*current_pcr == '\0') + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("trailing comma at the end of PCR list")); + } + + if (i == TPM_MAX_PCRS) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("too many PCRs in PCR list, the maximum number of PCRs is %u"), TPM_MAX_PCRS); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_tpm2_protector_parse_asymmetric (const char *value, + grub_srk_type_t *srk_type) +{ + if (grub_strcasecmp (value, "ECC") == 0 || + grub_strcasecmp (value, "ECC_NIST_P256") == 0) + { + srk_type->type = TPM_ALG_ECC; + srk_type->detail.ecc_curve = TPM_ECC_NIST_P256; + } + else if (grub_strcasecmp (value, "RSA") == 0 || + grub_strcasecmp (value, "RSA2048") == 0) + { + srk_type->type = TPM_ALG_RSA; + srk_type->detail.rsa_bits = 2048; + } + else + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("value '%s' is not a valid asymmetric key type"), value); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_tpm2_protector_parse_bank (const char *value, TPM_ALG_ID_t *bank) +{ + if (grub_strcasecmp (value, "SHA1") == 0) + *bank = TPM_ALG_SHA1; + else if (grub_strcasecmp (value, "SHA256") == 0) + *bank = TPM_ALG_SHA256; + else if (grub_strcasecmp (value, "SHA384") == 0) + *bank = TPM_ALG_SHA384; + else if (grub_strcasecmp (value, "SHA512") == 0) + *bank = TPM_ALG_SHA512; + else + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("value '%s' is not a valid PCR bank"), value); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_tpm2_protector_parse_tpm_handle (const char *value, TPM_HANDLE_t *handle) +{ + grub_uint64_t num; + const char *str_end; + + num = grub_strtoul (value, &str_end, 0); + if (*value == '\0' || *str_end != '\0') + return grub_error (GRUB_ERR_BAD_NUMBER, N_("TPM handle value '%s' is not a number"), value); + + if (num > GRUB_UINT_MAX) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("value %llu is too large to be a TPM handle, TPM handles are unsigned 32-bit integers"), (unsigned long long)num); + + *handle = (TPM_HANDLE_t) num; + + return GRUB_ERR_NONE; +} diff --git a/grub-core/commands/tpm2_key_protector/module.c b/grub-core/commands/tpm2_key_protector/module.c new file mode 100644 index 000000000..70d4d0df7 --- /dev/null +++ b/grub-core/commands/tpm2_key_protector/module.c @@ -0,0 +1,1153 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 "tpm2_args.h" +#include "tpm2.h" +#include "tpm2key.h" + +GRUB_MOD_LICENSE ("GPLv3+"); + +typedef enum tpm2_protector_mode +{ + TPM2_PROTECTOR_MODE_UNSET, + TPM2_PROTECTOR_MODE_SRK, + TPM2_PROTECTOR_MODE_NV +} tpm2_protector_mode_t; + +typedef enum tpm2_protector_options +{ + OPTION_MODE, + OPTION_PCRS, + OPTION_BANK, + OPTION_TPM2KEY, + OPTION_KEYFILE, + OPTION_SRK, + OPTION_ASYMMETRIC, + OPTION_NVINDEX +} tpm2_protector_options_t; + +typedef struct tpm2_protector_context +{ + tpm2_protector_mode_t mode; + grub_uint8_t pcrs[TPM_MAX_PCRS]; + grub_uint8_t pcr_count; + grub_srk_type_t srk_type; + TPM_ALG_ID_t bank; + const char *tpm2key; + const char *keyfile; + TPM_HANDLE_t srk; + TPM_HANDLE_t nv; +} tpm2_protector_context_t; + +static const struct grub_arg_option tpm2_protector_init_cmd_options[] = + { + /* Options for all modes */ + { + .longarg = "mode", + .shortarg = 'm', + .flags = 0, + .arg = NULL, + .type = ARG_TYPE_STRING, + .doc = + N_("Unseal key using SRK ('srk') (default) or retrieve it from an NV " + "Index ('nv')."), + }, + { + .longarg = "pcrs", + .shortarg = 'p', + .flags = 0, + .arg = NULL, + .type = ARG_TYPE_STRING, + .doc = + N_("Comma-separated list of PCRs used to authorize key release " + "e.g., '7,11'. (default: 7)"), + }, + { + .longarg = "bank", + .shortarg = 'b', + .flags = 0, + .arg = NULL, + .type = ARG_TYPE_STRING, + .doc = + N_("Bank of PCRs used to authorize key release: " + "SHA1, SHA256, SHA384 or SHA512. (default: SHA256)"), + }, + /* SRK-mode options */ + { + .longarg = "tpm2key", + .shortarg = 'T', + .flags = 0, + .arg = NULL, + .type = ARG_TYPE_STRING, + .doc = + N_("In SRK mode, path to the key file in the TPM 2.0 Key File format " + "to unseal using the TPM (e.g., (hd0,gpt1)/boot/grub2/sealed.tpm)."), + }, + { + .longarg = "keyfile", + .shortarg = 'k', + .flags = 0, + .arg = NULL, + .type = ARG_TYPE_STRING, + .doc = + N_("In SRK mode, path to the key file in the raw format to unseal " + "using the TPM (e.g., (hd0,gpt1)/boot/grub2/sealed.key). " + "(Mainly for backward compatibility. Please use '--tpm2key'.)"), + }, + { + .longarg = "srk", + .shortarg = 's', + .flags = 0, + .arg = NULL, + .type = ARG_TYPE_STRING, + .doc = + N_("In SRK mode, the SRK handle if the SRK is persistent."), + }, + { + .longarg = "asymmetric", + .shortarg = 'a', + .flags = 0, + .arg = NULL, + .type = ARG_TYPE_STRING, + .doc = + N_("In SRK mode, the type of SRK: RSA (RSA2048) and ECC (ECC_NIST_P256)" + "(default: ECC)"), + }, + /* NV Index-mode options */ + { + .longarg = "nvindex", + .shortarg = 'n', + .flags = 0, + .arg = NULL, + .type = ARG_TYPE_STRING, + .doc = + N_("Required in NV Index mode, the NV handle to read which must " + "readily exist on the TPM and which contains the key."), + }, + /* End of list */ + {0, 0, 0, 0, 0, 0} + }; + +static grub_extcmd_t tpm2_protector_init_cmd; +static grub_extcmd_t tpm2_protector_clear_cmd; +static tpm2_protector_context_t tpm2_protector_ctx = {0}; + +static grub_err_t +tpm2_protector_srk_read_file (const char *filepath, void **buffer, grub_size_t *buffer_size) +{ + grub_file_t file; + grub_off_t file_size; + void *read_buffer; + grub_off_t read_n; + grub_err_t err; + + /* + * Using GRUB_FILE_TYPE_SIGNATURE ensures we do not hash the keyfile into PCR9 + * otherwise we'll never be able to predict the value of PCR9 at unseal time + */ + file = grub_file_open (filepath, GRUB_FILE_TYPE_SIGNATURE); + if (file == NULL) + { + /* Push errno from grub_file_open() into the error message stack */ + grub_error_push(); + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("could not open file: %s"), filepath); + goto error; + } + + file_size = grub_file_size (file); + if (file_size == 0) + { + err = grub_error (GRUB_ERR_OUT_OF_RANGE, N_("could not read file size: %s"), filepath); + goto error; + } + + read_buffer = grub_malloc (file_size); + if (read_buffer == NULL) + { + err = grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("could not allocate buffer for %s"), filepath); + goto error; + } + + read_n = grub_file_read (file, read_buffer, file_size); + if (read_n != file_size) + { + grub_free (read_buffer); + err = grub_error (GRUB_ERR_FILE_READ_ERROR, N_("could not retrieve file contents: %s"), filepath); + goto error; + } + + *buffer = read_buffer; + *buffer_size = file_size; + + err = GRUB_ERR_NONE; + + error: + if (file != NULL) + grub_file_close (file); + + return err; +} + +static grub_err_t +tpm2_protector_srk_unmarshal_keyfile (void *sealed_key, + grub_size_t sealed_key_size, + tpm2_sealed_key_t *sk) +{ + struct grub_tpm2_buffer buf; + + grub_tpm2_buffer_init (&buf); + if (sealed_key_size > buf.cap) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("sealed key larger than %llu bytes"), (unsigned long long)buf.cap); + + grub_memcpy (buf.data, sealed_key, sealed_key_size); + buf.size = sealed_key_size; + + grub_Tss2_MU_TPM2B_PUBLIC_Unmarshal (&buf, &sk->public); + grub_Tss2_MU_TPM2B_PRIVATE_Unmarshal (&buf, &sk->private); + + if (buf.error != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("malformed TPM wire key file")); + + return GRUB_ERR_NONE; +} + +static grub_err_t +tpm2_protector_srk_unmarshal_tpm2key (void *sealed_key, + grub_size_t sealed_key_size, + tpm2key_policy_t *policy_seq, + tpm2key_authpolicy_t *authpol_seq, + grub_uint8_t *rsaparent, + grub_uint32_t *parent, + tpm2_sealed_key_t *sk) +{ + asn1_node tpm2key = NULL; + grub_uint8_t rsaparent_tmp; + grub_uint32_t parent_tmp; + void *sealed_pub = NULL; + grub_size_t sealed_pub_size; + void *sealed_priv = NULL; + grub_size_t sealed_priv_size; + struct grub_tpm2_buffer buf; + grub_err_t err; + + /* + * Start to parse the tpm2key file + * TPMKey ::= SEQUENCE { + * type OBJECT IDENTIFIER, + * emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL, + * policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL, + * secret [2] EXPLICIT OCTET STRING OPTIONAL, + * authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL, + * description [4] EXPLICIT UTF8String OPTIONAL, + * rsaParent [5] EXPLICIT BOOLEAN OPTIONAL, + * parent INTEGER, + * pubkey OCTET STRING, + * privkey OCTET STRING + * } + */ + err = grub_tpm2key_start_parsing (&tpm2key, sealed_key, sealed_key_size); + if (err != GRUB_ERR_NONE) + return err; + + /* + * Retrieve the policy sequence from 'policy' + * policy_seq will be NULL when 'policy' is not available + */ + err = grub_tpm2key_get_policy_seq (tpm2key, policy_seq); + if (err != GRUB_ERR_NONE) + goto error; + + /* + * Retrieve the authpolicy sequence from 'authPolicy' + * authpol_seq will be NULL when 'authPolicy' is not available + */ + err = grub_tpm2key_get_authpolicy_seq (tpm2key, authpol_seq); + if (err != GRUB_ERR_NONE) + goto error; + + /* Retrieve rsaParent */ + err = grub_tpm2key_get_rsaparent (tpm2key, &rsaparent_tmp); + if (err != GRUB_ERR_NONE) + goto error; + + *rsaparent = rsaparent_tmp; + + /* Retrieve the parent handle */ + err = grub_tpm2key_get_parent (tpm2key, &parent_tmp); + if (err != GRUB_ERR_NONE) + goto error; + + /* The parent handle should be either PERMANENT or PERSISTENT. */ + if (!TPM_HT_IS_PERMANENT (parent_tmp) && !TPM_HT_IS_PERSISTENT (parent_tmp)) + { + err = GRUB_ERR_OUT_OF_RANGE; + goto error; + } + + *parent = parent_tmp; + + /* Retrieve the public part of the sealed key */ + err = grub_tpm2key_get_pubkey (tpm2key, &sealed_pub, &sealed_pub_size); + if (err != GRUB_ERR_NONE) + goto error; + + /* Retrieve the private part of the sealed key */ + err = grub_tpm2key_get_privkey (tpm2key, &sealed_priv, &sealed_priv_size); + if (err != GRUB_ERR_NONE) + goto error; + + /* Unmarshal the sealed key */ + grub_tpm2_buffer_init (&buf); + if (sealed_pub_size + sealed_priv_size > buf.cap) + { + err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("sealed key larger than %llu bytes"), (unsigned long long)buf.cap); + goto error; + } + + grub_tpm2_buffer_pack (&buf, sealed_pub, sealed_pub_size); + grub_tpm2_buffer_pack (&buf, sealed_priv, sealed_priv_size); + + buf.offset = 0; + + grub_Tss2_MU_TPM2B_PUBLIC_Unmarshal (&buf, &sk->public); + grub_Tss2_MU_TPM2B_PRIVATE_Unmarshal (&buf, &sk->private); + + if (buf.error != 0) + { + err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("malformed TPM 2.0 key file")); + goto error; + } + + err = GRUB_ERR_NONE; + + error: + /* End the parsing */ + grub_tpm2key_end_parsing (tpm2key); + grub_free (sealed_pub); + grub_free (sealed_priv); + + return err; +} + +/* Check if the SRK exists in the specified handle */ +static grub_err_t +tpm2_protector_srk_check (const TPM_HANDLE_t srk_handle) +{ + TPM_RC_t rc; + TPM2B_PUBLIC_t public; + + /* Find SRK */ + rc = grub_tpm2_readpublic (srk_handle, NULL, &public); + if (rc == TPM_RC_SUCCESS) + return GRUB_ERR_NONE; + + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to retrieve SRK from 0x%x (TPM2_ReadPublic: 0x%x)", srk_handle, rc); +} + +/* Get the SRK with the template */ +static grub_err_t +tpm2_protector_srk_get (const grub_srk_type_t srk_type, + const TPM_HANDLE_t parent, + TPM_HANDLE_t *srk_handle) +{ + TPM_RC_t rc; + TPMT_PUBLIC_PARMS_t parms = {0}; + TPMS_AUTH_COMMAND_t authCommand = {0}; + TPM2B_SENSITIVE_CREATE_t inSensitive = {0}; + TPM2B_PUBLIC_t inPublic = {0}; + TPM2B_DATA_t outsideInfo = {0}; + TPML_PCR_SELECTION_t creationPcr = {0}; + TPM2B_PUBLIC_t outPublic = {0}; + TPM2B_CREATION_DATA_t creationData = {0}; + TPM2B_DIGEST_t creationHash = {0}; + TPMT_TK_CREATION_t creationTicket = {0}; + TPM2B_NAME_t srkName = {0}; + TPM_HANDLE_t tmp_handle = 0; + + inPublic.publicArea.type = srk_type.type; + inPublic.publicArea.nameAlg = TPM_ALG_SHA256; + inPublic.publicArea.objectAttributes.restricted = 1; + inPublic.publicArea.objectAttributes.userWithAuth = 1; + inPublic.publicArea.objectAttributes.decrypt = 1; + inPublic.publicArea.objectAttributes.fixedTPM = 1; + inPublic.publicArea.objectAttributes.fixedParent = 1; + inPublic.publicArea.objectAttributes.sensitiveDataOrigin = 1; + inPublic.publicArea.objectAttributes.noDA = 1; + + if (srk_type.type == TPM_ALG_RSA) + { + inPublic.publicArea.parameters.rsaDetail.symmetric.algorithm = TPM_ALG_AES; + inPublic.publicArea.parameters.rsaDetail.symmetric.keyBits.aes = 128; + inPublic.publicArea.parameters.rsaDetail.symmetric.mode.aes = TPM_ALG_CFB; + inPublic.publicArea.parameters.rsaDetail.scheme.scheme = TPM_ALG_NULL; + inPublic.publicArea.parameters.rsaDetail.keyBits = srk_type.detail.rsa_bits; + inPublic.publicArea.parameters.rsaDetail.exponent = 0; + } + else if (srk_type.type == TPM_ALG_ECC) + { + inPublic.publicArea.parameters.eccDetail.symmetric.algorithm = TPM_ALG_AES; + inPublic.publicArea.parameters.eccDetail.symmetric.keyBits.aes = 128; + inPublic.publicArea.parameters.eccDetail.symmetric.mode.aes = TPM_ALG_CFB; + inPublic.publicArea.parameters.eccDetail.scheme.scheme = TPM_ALG_NULL; + inPublic.publicArea.parameters.eccDetail.curveID = srk_type.detail.ecc_curve; + inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL; + } + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown SRK algorithm"); + + /* Test the parameters before SRK generation */ + parms.type = srk_type.type; + grub_memcpy (&parms.parameters, &inPublic.publicArea.parameters, + sizeof (TPMU_PUBLIC_PARMS_t)); + + rc = grub_tpm2_testparms (&parms, NULL); + if (rc != TPM_RC_SUCCESS) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unsupported SRK template (TPM2_TestParms: 0x%x)", rc); + + /* Create SRK */ + authCommand.sessionHandle = TPM_RS_PW; + rc = grub_tpm2_createprimary (parent, &authCommand, &inSensitive, &inPublic, + &outsideInfo, &creationPcr, &tmp_handle, &outPublic, + &creationData, &creationHash, &creationTicket, + &srkName, NULL); + if (rc != TPM_RC_SUCCESS) + return grub_error (GRUB_ERR_BAD_DEVICE, "could not create SRK (TPM2_CreatePrimary: 0x%x)", rc); + + *srk_handle = tmp_handle; + + return GRUB_ERR_NONE; +} + +/* + * Load the SRK from the persistent handle or create one with a given type of + * template, and then associate the sealed key with the SRK + * Return values: + * - GRUB_ERR_NONE: Everything is fine. + * - GRUB_ERR_BAD_ARGUMENT: The SRK doesn't match. Try another one. + * - Other: Something went wrong. + */ +static grub_err_t +tpm2_protector_srk_load (const grub_srk_type_t srk_type, + const tpm2_sealed_key_t *sealed_key, + const TPM_HANDLE_t parent, + TPM_HANDLE_t *sealed_handle, + TPM_HANDLE_t *srk_handle) +{ + TPMS_AUTH_COMMAND_t authCmd = {0}; + TPM2B_NAME_t name = {0}; + TPM_RC_t rc; + grub_err_t err; + + if (srk_handle == NULL) + return GRUB_ERR_BUG; + + if (*srk_handle != 0) + { + err = tpm2_protector_srk_check (*srk_handle); + if (err != GRUB_ERR_NONE) + return err; + } + else + { + err = tpm2_protector_srk_get (srk_type, parent, srk_handle); + if (err != GRUB_ERR_NONE) + return err; + } + + /* Load the sealed key and associate it with the SRK */ + authCmd.sessionHandle = TPM_RS_PW; + rc = grub_tpm2_load (*srk_handle, &authCmd, &sealed_key->private, &sealed_key->public, + sealed_handle, &name, NULL); + /* + * If TPM2_Load returns (TPM_RC_INTEGRITY | TPM_RC_P | TPM_RC_1), then it + * implies the wrong SRK is used. + */ + if (rc == (TPM_RC_INTEGRITY | TPM_RC_P | TPM_RC_1)) + { + err = grub_error (GRUB_ERR_BAD_ARGUMENT, "SRK not matched"); + goto error; + } + else if (rc != TPM_RC_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_DEVICE, "failed to load sealed key (TPM2_Load: 0x%x)", rc); + goto error; + } + + return GRUB_ERR_NONE; + + error: + if (!TPM_HT_IS_PERSISTENT (*srk_handle)) + grub_tpm2_flushcontext (*srk_handle); + + return err; +} + +static const char * +srk_type_to_name (grub_srk_type_t srk_type) +{ + if (srk_type.type == TPM_ALG_ECC && srk_type.detail.ecc_curve == TPM_ECC_NIST_P256) + return "ECC_NIST_P256"; + else if (srk_type.type == TPM_ALG_RSA && srk_type.detail.rsa_bits == 2048) + return "RSA2048"; + + return "Unknown"; +} + +static grub_err_t +tpm2_protector_load_key (const tpm2_protector_context_t *ctx, + const tpm2_sealed_key_t *sealed_key, + const TPM_HANDLE_t parent_handle, + TPM_HANDLE_t *sealed_handle, + TPM_HANDLE_t *srk_handle) +{ + grub_err_t err; + int i; + grub_srk_type_t fallback_srks[] = { + { + .type = TPM_ALG_ECC, + .detail.ecc_curve = TPM_ECC_NIST_P256, + }, + { + .type = TPM_ALG_RSA, + .detail.rsa_bits = 2048, + }, + { + .type = TPM_ALG_ERROR, + } + }; + + /* Try the given persistent SRK if exists */ + if (*srk_handle != 0) + { + err = tpm2_protector_srk_load (ctx->srk_type, sealed_key, + parent_handle, sealed_handle, + srk_handle); + if (err != GRUB_ERR_BAD_ARGUMENT) + return err; + + grub_print_error (); + grub_printf ("Trying the specified SRK algorithm: %s\n", srk_type_to_name (ctx->srk_type)); + grub_errno = GRUB_ERR_NONE; + *srk_handle = 0; + } + + /* Try the specified algorithm for the SRK template */ + if (*srk_handle == 0) + { + err = tpm2_protector_srk_load (ctx->srk_type, sealed_key, + parent_handle, sealed_handle, + srk_handle); + if (err != GRUB_ERR_BAD_ARGUMENT) + return err; + + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + *srk_handle = 0; + } + + /* Try all the fallback SRK templates */ + for (i = 0; fallback_srks[i].type != TPM_ALG_ERROR; i++) + { + /* Skip the specified algorithm */ + if (fallback_srks[i].type == ctx->srk_type.type && + (fallback_srks[i].detail.rsa_bits == ctx->srk_type.detail.rsa_bits || + fallback_srks[i].detail.ecc_curve == ctx->srk_type.detail.ecc_curve)) + continue; + + grub_printf ("Trying fallback %s template\n", srk_type_to_name (fallback_srks[i])); + + *srk_handle = 0; + + err = tpm2_protector_srk_load (fallback_srks[i], sealed_key, + parent_handle, sealed_handle, + srk_handle); + if (err != GRUB_ERR_BAD_ARGUMENT) + return err; + + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + } + + return err; +} + +static grub_err_t +tpm2_protector_policypcr (TPMI_SH_AUTH_SESSION_t session, struct grub_tpm2_buffer *cmd_buf) +{ + TPM2B_DIGEST_t pcr_digest; + TPML_PCR_SELECTION_t pcr_sel; + TPM_RC_t rc; + + grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (cmd_buf, &pcr_digest); + grub_Tss2_MU_TPML_PCR_SELECTION_Unmarshal (cmd_buf, &pcr_sel); + if (cmd_buf->error != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to unmarshal commandPolicy for TPM2_PolicyPCR"); + + rc = grub_tpm2_policypcr (session, NULL, &pcr_digest, &pcr_sel, NULL); + if (rc != TPM_RC_SUCCESS) + return grub_error (GRUB_ERR_BAD_DEVICE, "failed to submit PCR policy (TPM2_PolicyPCR: 0x%x)", rc); + + return GRUB_ERR_NONE; +} + +static grub_err_t +tpm2_protector_enforce_policy (tpm2key_policy_t policy, TPMI_SH_AUTH_SESSION_t session) +{ + struct grub_tpm2_buffer buf; + grub_err_t err; + + grub_tpm2_buffer_init (&buf); + if (policy->cmd_policy_len > buf.cap) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "CommandPolicy larger than TPM buffer"); + + grub_memcpy (buf.data, policy->cmd_policy, policy->cmd_policy_len); + buf.size = policy->cmd_policy_len; + + switch (policy->cmd_code) + { + case TPM_CC_PolicyPCR: + err = tpm2_protector_policypcr (session, &buf); + break; + default: + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown TPM Command: 0x%x", policy->cmd_code); + } + + return err; +} + +static grub_err_t +tpm2_protector_enforce_policy_seq (tpm2key_policy_t policy_seq, TPMI_SH_AUTH_SESSION_t session) +{ + tpm2key_policy_t policy; + grub_err_t err; + + FOR_LIST_ELEMENTS (policy, policy_seq) + { + err = tpm2_protector_enforce_policy (policy, session); + if (err != GRUB_ERR_NONE) + return err; + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +tpm2_protector_simple_policy_seq (const tpm2_protector_context_t *ctx, + tpm2key_policy_t *policy_seq) +{ + tpm2key_policy_t policy = NULL; + struct grub_tpm2_buffer buf; + TPML_PCR_SELECTION_t pcr_sel = { + .count = 1, + .pcrSelections = { + { + .hash = ctx->bank, + .sizeOfSelect = 3, + .pcrSelect = {0} + }, + } + }; + grub_uint8_t i; + grub_err_t err; + + if (policy_seq == NULL) + return GRUB_ERR_BAD_ARGUMENT; + + grub_tpm2_buffer_init (&buf); + + for (i = 0; i < ctx->pcr_count; i++) + TPMS_PCR_SELECTION_SelectPCR (&pcr_sel.pcrSelections[0], ctx->pcrs[i]); + + grub_tpm2_buffer_pack_u16 (&buf, 0); + grub_Tss2_MU_TPML_PCR_SELECTION_Marshal (&buf, &pcr_sel); + + if (buf.error != 0) + return GRUB_ERR_BAD_ARGUMENT; + + policy = grub_malloc (sizeof(struct tpm2key_policy)); + if (policy == NULL) + { + err = GRUB_ERR_OUT_OF_MEMORY; + goto error; + } + policy->cmd_code = TPM_CC_PolicyPCR; + policy->cmd_policy = grub_malloc (buf.size); + if (policy->cmd_policy == NULL) + { + err = GRUB_ERR_OUT_OF_MEMORY; + goto error; + } + grub_memcpy (policy->cmd_policy, buf.data, buf.size); + policy->cmd_policy_len = buf.size; + + grub_list_push (GRUB_AS_LIST_P (policy_seq), GRUB_AS_LIST (policy)); + + return GRUB_ERR_NONE; + + error: + grub_free (policy); + + return err; +} + +static grub_err_t +tpm2_protector_unseal (tpm2key_policy_t policy_seq, TPM_HANDLE_t sealed_handle, + grub_uint8_t **key, grub_size_t *key_size) +{ + TPMS_AUTH_COMMAND_t authCmd = {0}; + TPM2B_SENSITIVE_DATA_t data; + TPM2B_NONCE_t nonceCaller = {0}; + TPMT_SYM_DEF_t symmetric = {0}; + TPMI_SH_AUTH_SESSION_t session; + grub_uint8_t *key_out; + TPM_RC_t rc; + grub_err_t err; + + /* Start Auth Session */ + nonceCaller.size = TPM_SHA256_DIGEST_SIZE; + symmetric.algorithm = TPM_ALG_NULL; + rc = grub_tpm2_startauthsession (TPM_RH_NULL, TPM_RH_NULL, NULL, &nonceCaller, NULL, + TPM_SE_POLICY, &symmetric, TPM_ALG_SHA256, + &session, NULL, NULL); + if (rc != TPM_RC_SUCCESS) + return grub_error (GRUB_ERR_BAD_DEVICE, "failed to start auth session (TPM2_StartAuthSession: 0x%x)", rc); + + /* Enforce the policy command sequence */ + err = tpm2_protector_enforce_policy_seq (policy_seq, session); + if (err != GRUB_ERR_NONE) + goto error; + + /* Unseal Sealed Key */ + authCmd.sessionHandle = session; + rc = grub_tpm2_unseal (sealed_handle, &authCmd, &data, NULL); + if (rc != TPM_RC_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_DEVICE, "failed to unseal sealed key (TPM2_Unseal: 0x%x)", rc); + goto error; + } + + /* Epilogue */ + key_out = grub_malloc (data.size); + if (key_out == NULL) + { + err = grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("no memory left to allocate unlock key buffer")); + goto error; + } + + grub_memcpy (key_out, data.buffer, data.size); + + *key = key_out; + *key_size = data.size; + + err = GRUB_ERR_NONE; + + error: + grub_tpm2_flushcontext (session); + + return err; +} + +static grub_err_t +tpm2_protector_srk_recover (const tpm2_protector_context_t *ctx, + grub_uint8_t **key, grub_size_t *key_size) +{ + tpm2_sealed_key_t sealed_key = {0}; + void *file_bytes = NULL; + grub_size_t file_size = 0; + grub_uint8_t rsaparent = 0; + TPM_HANDLE_t parent_handle = 0; + TPM_HANDLE_t srk_handle = 0; + TPM_HANDLE_t sealed_handle = 0; + tpm2key_policy_t policy_seq = NULL; + tpm2key_authpolicy_t authpol = NULL; + tpm2key_authpolicy_t authpol_seq = NULL; + grub_err_t err; + + /* + * Retrieve sealed key, parent handle, policy sequence, and authpolicy + * sequence from the key file + */ + if (ctx->tpm2key != NULL) + { + err = tpm2_protector_srk_read_file (ctx->tpm2key, &file_bytes, + &file_size); + if (err != GRUB_ERR_NONE) + return err; + + err = tpm2_protector_srk_unmarshal_tpm2key (file_bytes, + file_size, + &policy_seq, + &authpol_seq, + &rsaparent, + &parent_handle, + &sealed_key); + if (err != GRUB_ERR_NONE) + goto exit1; + + if (rsaparent == 1) + { + tpm2_protector_context_t *ctx_w; + + /* Overwrite the SRK type as noted in the key */ + ctx_w = (tpm2_protector_context_t *)ctx; + ctx_w->srk_type.type = TPM_ALG_RSA; + ctx_w->srk_type.detail.rsa_bits = 2048; + } + } + else + { + err = tpm2_protector_srk_read_file (ctx->keyfile, &file_bytes, &file_size); + if (err != GRUB_ERR_NONE) + return err; + + parent_handle = TPM_RH_OWNER; + err = tpm2_protector_srk_unmarshal_keyfile (file_bytes, file_size, &sealed_key); + if (err != GRUB_ERR_NONE) + goto exit1; + } + + /* Set the SRK handle if it is specified with '--srk' or inside the key file */ + if (ctx->srk != 0) + srk_handle = ctx->srk; + else if (TPM_HT_IS_PERSISTENT (parent_handle)) + srk_handle = parent_handle; + + /* Load the sealed key into TPM and associate it with the SRK */ + err = tpm2_protector_load_key (ctx, &sealed_key, parent_handle, &sealed_handle, &srk_handle); + if (err != GRUB_ERR_NONE) + goto exit1; + + /* + * Set err to an error code to trigger the standalone policy sequence + * if there is no authpolicy sequence + */ + err = GRUB_ERR_READ_ERROR; + + /* Iterate the authpolicy sequence to find one that unseals the key */ + FOR_LIST_ELEMENTS (authpol, authpol_seq) + { + err = tpm2_protector_unseal (authpol->policy_seq, sealed_handle, key, key_size); + if (err == GRUB_ERR_NONE) + break; + + /* + * Push the error message into the grub_error stack + * Note: The grub_error stack may overflow if there are too many policy + * sequences. Anyway, we still can keep the error messages from + * the first few policy sequences which are usually most likely to + * unseal the key. + */ + grub_error_push(); + } + + /* Give the standalone policy sequence a try */ + if (err != GRUB_ERR_NONE) + { + /* + * Create a basic policy sequence based on the given PCR selection if the + * key file doesn't provide one + */ + if (policy_seq == NULL) + { + err = tpm2_protector_simple_policy_seq (ctx, &policy_seq); + if (err != GRUB_ERR_NONE) + goto exit2; + } + + err = tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size); + } + + /* Pop error messages on success */ + if (err == GRUB_ERR_NONE) + while (grub_error_pop ()); + + exit2: + grub_tpm2_flushcontext (sealed_handle); + + if (!TPM_HT_IS_PERSISTENT (srk_handle)) + grub_tpm2_flushcontext (srk_handle); + + exit1: + grub_tpm2key_free_policy_seq (policy_seq); + grub_tpm2key_free_authpolicy_seq (authpol_seq); + grub_free (file_bytes); + return err; +} + +static grub_err_t +tpm2_protector_nv_recover (const tpm2_protector_context_t *ctx __attribute__ ((unused)), + grub_uint8_t **key __attribute__ ((unused)), + grub_size_t *key_size __attribute__ ((unused))) +{ + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "NV Index mode is not implemented yet"); +} + +static grub_err_t +tpm2_protector_recover (const tpm2_protector_context_t *ctx, + grub_uint8_t **key, grub_size_t *key_size) +{ + switch (ctx->mode) + { + case TPM2_PROTECTOR_MODE_SRK: + return tpm2_protector_srk_recover (ctx, key, key_size); + case TPM2_PROTECTOR_MODE_NV: + return tpm2_protector_nv_recover (ctx, key, key_size); + default: + return GRUB_ERR_BAD_ARGUMENT; + } +} + +static grub_err_t +tpm2_protector_recover_key (grub_uint8_t **key, grub_size_t *key_size) +{ + /* Expect a call to tpm2_protector_init before anybody tries to use us */ + if (tpm2_protector_ctx.mode == TPM2_PROTECTOR_MODE_UNSET) + return grub_error (GRUB_ERR_INVALID_COMMAND, N_("cannot use TPM2 key protector without initializing it, call tpm2_protector_init first")); + + if (key == NULL || key_size == NULL) + return GRUB_ERR_BAD_ARGUMENT; + + return tpm2_protector_recover (&tpm2_protector_ctx, key, key_size); +} + +static grub_err_t +tpm2_protector_check_args (tpm2_protector_context_t *ctx) +{ + if (ctx->mode == TPM2_PROTECTOR_MODE_UNSET) + ctx->mode = TPM2_PROTECTOR_MODE_SRK; + + /* Checks for SRK mode */ + if (ctx->mode == TPM2_PROTECTOR_MODE_SRK && + (ctx->keyfile == NULL && ctx->tpm2key == NULL)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("in SRK mode, a key file must be specified: --tpm2key/-T or --keyfile/-k")); + + if (ctx->mode == TPM2_PROTECTOR_MODE_SRK && + (ctx->keyfile != NULL && ctx->tpm2key != NULL)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("in SRK mode, please specify a key file with only --tpm2key/-T or --keyfile/-k")); + + if (ctx->mode == TPM2_PROTECTOR_MODE_SRK && ctx->nv != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("in SRK mode, an NV Index cannot be specified")); + + /* Checks for NV mode */ + if (ctx->mode == TPM2_PROTECTOR_MODE_NV && ctx->nv == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("in NV Index mode, an NV Index must be specified: --nvindex or -n")); + + if (ctx->mode == TPM2_PROTECTOR_MODE_NV && + (ctx->tpm2key != NULL || ctx->keyfile != NULL)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("in NV Index mode, a keyfile cannot be specified")); + + if (ctx->mode == TPM2_PROTECTOR_MODE_NV && ctx->srk != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("in NV Index mode, an SRK cannot be specified")); + + if (ctx->mode == TPM2_PROTECTOR_MODE_NV && + ctx->srk_type.type != TPM_ALG_ERROR) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("in NV Index mode, an asymmetric key type cannot be specified")); + + /* Defaults assignment */ + if (ctx->bank == TPM_ALG_ERROR) + ctx->bank = TPM_ALG_SHA256; + + if (ctx->pcr_count == 0) + { + ctx->pcrs[0] = 7; + ctx->pcr_count = 1; + } + + if (ctx->mode == TPM2_PROTECTOR_MODE_SRK && + ctx->srk_type.type == TPM_ALG_ERROR) + { + ctx->srk_type.type = TPM_ALG_ECC; + ctx->srk_type.detail.ecc_curve = TPM_ECC_NIST_P256; + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +tpm2_protector_parse_file (const char *value, const char **file) +{ + if (grub_strlen (value) == 0) + return GRUB_ERR_BAD_ARGUMENT; + + *file = grub_strdup (value); + if (*file == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("no memory to duplicate file path")); + + return GRUB_ERR_NONE; +} + +static grub_err_t +tpm2_protector_parse_mode (const char *value, tpm2_protector_mode_t *mode) +{ + if (grub_strcmp (value, "srk") == 0) + *mode = TPM2_PROTECTOR_MODE_SRK; + else if (grub_strcmp (value, "nv") == 0) + *mode = TPM2_PROTECTOR_MODE_NV; + else + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("value '%s' is not a valid TPM2 key protector mode"), value); + + return GRUB_ERR_NONE; +} + +static grub_err_t +tpm2_protector_init_cmd_handler (grub_extcmd_context_t ctxt, int argc, + char **args __attribute__ ((unused))) +{ + struct grub_arg_list *state = ctxt->state; + grub_err_t err; + + if (argc) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("the TPM2 key protector does not accept any non-option arguments (i.e., like -o and/or --option only)")); + + grub_free ((void *) tpm2_protector_ctx.keyfile); + grub_memset (&tpm2_protector_ctx, 0, sizeof (tpm2_protector_ctx)); + + if (state[OPTION_MODE].set) /* mode */ + { + err = tpm2_protector_parse_mode (state[OPTION_MODE].arg, &tpm2_protector_ctx.mode); + if (err != GRUB_ERR_NONE) + return err; + } + + if (state[OPTION_PCRS].set) /* pcrs */ + { + err = grub_tpm2_protector_parse_pcrs (state[OPTION_PCRS].arg, + tpm2_protector_ctx.pcrs, + &tpm2_protector_ctx.pcr_count); + if (err != GRUB_ERR_NONE) + return err; + } + + if (state[OPTION_BANK].set) /* bank */ + { + err = grub_tpm2_protector_parse_bank (state[OPTION_BANK].arg, + &tpm2_protector_ctx.bank); + if (err != GRUB_ERR_NONE) + return err; + } + + if (state[OPTION_TPM2KEY].set) /* tpm2key */ + { + err = tpm2_protector_parse_file (state[OPTION_TPM2KEY].arg, + &tpm2_protector_ctx.tpm2key); + if (err != GRUB_ERR_NONE) + return err; + } + + if (state[OPTION_KEYFILE].set) /* keyfile */ + { + err = tpm2_protector_parse_file (state[OPTION_KEYFILE].arg, + &tpm2_protector_ctx.keyfile); + if (err != GRUB_ERR_NONE) + return err; + } + + if (state[OPTION_SRK].set) /* srk */ + { + err = grub_tpm2_protector_parse_tpm_handle (state[OPTION_SRK].arg, + &tpm2_protector_ctx.srk); + if (err != GRUB_ERR_NONE) + return err; + } + + if (state[OPTION_ASYMMETRIC].set) /* asymmetric */ + { + err = grub_tpm2_protector_parse_asymmetric (state[OPTION_ASYMMETRIC].arg, + &tpm2_protector_ctx.srk_type); + if (err != GRUB_ERR_NONE) + return err; + } + + if (state[OPTION_NVINDEX].set) /* nvindex */ + { + err = grub_tpm2_protector_parse_tpm_handle (state[OPTION_NVINDEX].arg, + &tpm2_protector_ctx.nv); + if (err != GRUB_ERR_NONE) + return err; + } + + err = tpm2_protector_check_args (&tpm2_protector_ctx); + + /* This command only initializes the protector, so nothing else to do. */ + + return err; +} + +static grub_err_t +tpm2_protector_clear_cmd_handler (grub_extcmd_context_t ctxt __attribute__ ((unused)), + int argc, char **args __attribute__ ((unused))) +{ + if (argc != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("tpm2_key_protector_clear accepts no arguments")); + + grub_free ((void *) tpm2_protector_ctx.keyfile); + grub_memset (&tpm2_protector_ctx, 0, sizeof (tpm2_protector_ctx)); + + return GRUB_ERR_NONE; +} + +static struct grub_key_protector tpm2_key_protector = + { + .name = "tpm2", + .recover_key = tpm2_protector_recover_key + }; + +GRUB_MOD_INIT (tpm2_key_protector) +{ + tpm2_protector_init_cmd = + grub_register_extcmd ("tpm2_key_protector_init", + tpm2_protector_init_cmd_handler, 0, + N_("[-m mode] " + "[-p pcr_list] " + "[-b pcr_bank] " + "[-T tpm2_key_file_path] " + "[-k sealed_key_file_path] " + "[-s srk_handle] " + "[-a asymmetric_key_type] " + "[-n nv_index]"), + N_("Initialize the TPM2 key protector."), + tpm2_protector_init_cmd_options); + tpm2_protector_clear_cmd = + grub_register_extcmd ("tpm2_key_protector_clear", + tpm2_protector_clear_cmd_handler, 0, NULL, + N_("Clear the TPM2 key protector if previously initialized."), + NULL); + grub_key_protector_register (&tpm2_key_protector); +} + +GRUB_MOD_FINI (tpm2_key_protector) +{ + grub_free ((void *) tpm2_protector_ctx.keyfile); + + grub_key_protector_unregister (&tpm2_key_protector); + grub_unregister_extcmd (tpm2_protector_clear_cmd); + grub_unregister_extcmd (tpm2_protector_init_cmd); +} diff --git a/grub-core/commands/tpm2_key_protector/tpm2.h b/grub-core/commands/tpm2_key_protector/tpm2.h new file mode 100644 index 000000000..1c1d871b4 --- /dev/null +++ b/grub-core/commands/tpm2_key_protector/tpm2.h @@ -0,0 +1,36 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 GRUB_TPM2_TPM2_HEADER +#define GRUB_TPM2_TPM2_HEADER 1 + +#include +#include +#include + +/* Well-Known Windows SRK handle */ +#define TPM2_SRK_HANDLE 0x81000001 + +struct tpm2_sealed_key { + TPM2B_PUBLIC_t public; + TPM2B_PRIVATE_t private; +}; +typedef struct tpm2_sealed_key tpm2_sealed_key_t; + +#endif /* ! GRUB_TPM2_TPM2_HEADER */ diff --git a/grub-core/commands/tpm2_key_protector/tpm2_args.h b/grub-core/commands/tpm2_key_protector/tpm2_args.h new file mode 100644 index 000000000..bdbf9f373 --- /dev/null +++ b/grub-core/commands/tpm2_key_protector/tpm2_args.h @@ -0,0 +1,49 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 GRUB_TPM2_INTERNAL_ARGS_HEADER +#define GRUB_TPM2_INTERNAL_ARGS_HEADER 1 + +#include + +#include "tpm2.h" + +struct grub_srk_type +{ + TPMI_ALG_PUBLIC_t type; + union { + TPM_KEY_BITS_t rsa_bits; + TPM_ECC_CURVE_t ecc_curve; + } detail; +}; +typedef struct grub_srk_type grub_srk_type_t; + +extern grub_err_t +grub_tpm2_protector_parse_pcrs (char *value, grub_uint8_t *pcrs, grub_uint8_t *pcr_count); + +extern grub_err_t +grub_tpm2_protector_parse_asymmetric (const char *value, grub_srk_type_t *srk_type); + +extern grub_err_t +grub_tpm2_protector_parse_bank (const char *value, TPM_ALG_ID_t *bank); + +extern grub_err_t +grub_tpm2_protector_parse_tpm_handle (const char *value, TPM_HANDLE_t *handle); + +#endif /* ! GRUB_TPM2_INTERNAL_ARGS_HEADER */ diff --git a/grub-core/commands/tpm2_key_protector/tpm2key.asn b/grub-core/commands/tpm2_key_protector/tpm2key.asn new file mode 100644 index 000000000..e1fb51545 --- /dev/null +++ b/grub-core/commands/tpm2_key_protector/tpm2key.asn @@ -0,0 +1,49 @@ +-- +-- GRUB: GRand Unified Bootloader +-- Copyright (C) 2024 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 . +-- +-- This file describes TPM 2.0 Key File format for libtasn1. +-- To generate tpm2key_asn1_tab.c: asn1Parser tpm2key.asn +-- +TPM2KEY {} +DEFINITIONS IMPLICIT TAGS ::= + +BEGIN + +TPMPolicy ::= SEQUENCE { + CommandCode [0] EXPLICIT INTEGER, + CommandPolicy [1] EXPLICIT OCTET STRING +} + +TPMAuthPolicy ::= SEQUENCE { + Name [0] EXPLICIT UTF8String OPTIONAL, + Policy [1] EXPLICIT SEQUENCE OF TPMPolicy +} + +TPMKey ::= SEQUENCE { + type OBJECT IDENTIFIER, + emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL, + policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL, + secret [2] EXPLICIT OCTET STRING OPTIONAL, + authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL, + description [4] EXPLICIT UTF8String OPTIONAL, + rsaParent [5] EXPLICIT BOOLEAN OPTIONAL, + parent INTEGER, + pubkey OCTET STRING, + privkey OCTET STRING +} + +END diff --git a/grub-core/commands/tpm2_key_protector/tpm2key.c b/grub-core/commands/tpm2_key_protector/tpm2key.c new file mode 100644 index 000000000..3b6001d84 --- /dev/null +++ b/grub-core/commands/tpm2_key_protector/tpm2key.c @@ -0,0 +1,499 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2023 SUSE LLC + * Copyright (C) 2024 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 "tpm2key.h" + +extern asn1_static_node tpm2key_asn1_tab[]; +const char *sealed_key_oid = "2.23.133.10.1.5"; + +static int +asn1_allocate_and_read (asn1_node node, const char *name, void **content, grub_size_t *content_size) +{ + grub_uint8_t *tmpstr = NULL; + int tmpstr_size = 0; + int ret; + + if (content == NULL) + return ASN1_MEM_ERROR; + + ret = asn1_read_value (node, name, NULL, &tmpstr_size); + if (ret != ASN1_MEM_ERROR) + return ret; + + tmpstr = grub_malloc (tmpstr_size); + if (tmpstr == NULL) + return ASN1_MEM_ERROR; + + ret = asn1_read_value (node, name, tmpstr, &tmpstr_size); + if (ret != ASN1_SUCCESS) + return ret; + + *content = tmpstr; + *content_size = tmpstr_size; + + return ASN1_SUCCESS; +} + +static int +asn1_read_uint32 (asn1_node node, const char *name, grub_uint32_t *out) +{ + grub_uint32_t tmp = 0; + grub_uint8_t *ptr; + void *data = NULL; + grub_size_t data_size; + int ret; + + ret = asn1_allocate_and_read (node, name, &data, &data_size); + if (ret != ASN1_SUCCESS) + return ret; + + /* + * ASN.1 INTEGER is encoded in the following format: + * + * TAG LENGTH OCTECTS + * + * The integer TAG is 02 and LENGTH is the number of followed OCTECTS in + * big endian. For example: + * + * 0x1: 02 01 01 + * 0xabcd: 02 02 ab cd + * + * To decribe 0x1, it only takes 1 octect, so LENGTH is 0x01 and the + * octect is 0x01. On the other hand, 0xabcd requires 2 octects: 'ab" and + * 'cd', so LENGTH is 0x02. + * + * This function only expects a uint32 integer, so it rejects any integer + * containing more than 4 octects. + */ + if (data_size > 4) + { + ret = ASN1_MEM_ERROR; + goto error; + } + + /* Copy the octects into 'tmp' to make it a big-endian uint32 */ + ptr = (grub_uint8_t *) &tmp + (4 - data_size); + grub_memcpy (ptr, data, data_size); + + /* Convert the big-endian integer to host uint32 */ + tmp = grub_be_to_cpu32 (tmp); + + *out = tmp; + error: + if (data) + grub_free (data); + return ret; +} + +grub_err_t +grub_tpm2key_start_parsing (asn1_node *parsed_tpm2key, void *data, grub_size_t size) +{ + asn1_node tpm2key; + asn1_node tpm2key_asn1 = NULL; + void *type_oid = NULL; + grub_size_t type_oid_size = 0; + void *empty_auth = NULL; + grub_size_t empty_auth_size = 0; + int tmp_size = 0; + int ret; + grub_err_t err; + + /* + * TPMKey ::= SEQUENCE { + * type OBJECT IDENTIFIER, + * emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL, + * policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL, + * secret [2] EXPLICIT OCTET STRING OPTIONAL, + * authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL, + * description [4] EXPLICIT UTF8String OPTIONAL, + * rsaParent [5] EXPLICIT BOOLEAN OPTIONAL, + * parent INTEGER, + * pubkey OCTET STRING, + * privkey OCTET STRING + * } + */ + ret = asn1_array2tree (tpm2key_asn1_tab, &tpm2key_asn1, NULL); + if (ret != ASN1_SUCCESS) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to parse TPM2KEY ASN.1 array"); + + ret = asn1_create_element (tpm2key_asn1, "TPM2KEY.TPMKey", &tpm2key); + if (ret != ASN1_SUCCESS) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to create TPM2KEY.TPMKey"); + + ret = asn1_der_decoding (&tpm2key, data, size, NULL); + if (ret != ASN1_SUCCESS) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to decode TPM2KEY DER"); + + /* Check if 'type' is Sealed Key or not */ + ret = asn1_allocate_and_read (tpm2key, "type", &type_oid, &type_oid_size); + if (ret != ASN1_SUCCESS) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a valid TPM2KEY file"); + + if (grub_memcmp (sealed_key_oid, type_oid, type_oid_size) != 0) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a valid TPM2KEY file"); + goto error; + } + + /* 'emptyAuth' must be 'TRUE' since we don't support password authorization */ + ret = asn1_allocate_and_read (tpm2key, "emptyAuth", &empty_auth, &empty_auth_size); + if (ret != ASN1_SUCCESS || grub_strncmp ("TRUE", empty_auth, empty_auth_size) != 0) + { + err = grub_error (GRUB_ERR_BAD_ARGUMENT, "emptyAuth not TRUE"); + goto error; + } + + /* 'secret' should not be in a sealed key */ + ret = asn1_read_value (tpm2key, "secret", NULL, &tmp_size); + if (ret != ASN1_ELEMENT_NOT_FOUND) + { + err = grub_error (GRUB_ERR_BAD_ARGUMENT, "\"secret\" not allowed for Sealed Key"); + goto error; + } + + *parsed_tpm2key = tpm2key; + + err = GRUB_ERR_NONE; + + error: + grub_free (type_oid); + grub_free (empty_auth); + + return err; +} + +void +grub_tpm2key_end_parsing (asn1_node tpm2key) +{ + asn1_delete_structure (&tpm2key); + tpm2key = NULL; +} + +grub_err_t +grub_tpm2key_get_rsaparent (asn1_node tpm2key, grub_uint8_t *rsaparent) +{ + void *bool_str = NULL; + grub_size_t bool_str_size = 0; + int ret; + + if (rsaparent == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "NULL pointer detected"); + + if (tpm2key == NULL) + return grub_error (GRUB_ERR_READ_ERROR, "invalid parent node"); + + ret = asn1_allocate_and_read (tpm2key, "rsaParent", &bool_str, &bool_str_size); + if (ret == ASN1_SUCCESS) + { + if (grub_strncmp ("TRUE", bool_str, bool_str_size) == 0) + *rsaparent = 1; + else + *rsaparent = 0; + } + else if (ret == ASN1_ELEMENT_NOT_FOUND) + *rsaparent = 0; + else + return grub_error (GRUB_ERR_READ_ERROR, "failed to retrieve rsaParent"); + + grub_free (bool_str); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_tpm2key_get_parent (asn1_node tpm2key, grub_uint32_t *parent) +{ + int ret; + + if (parent == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "NULL pointer detected"); + + if (tpm2key == NULL) + return grub_error (GRUB_ERR_READ_ERROR, "invalid parent node"); + + ret = asn1_read_uint32 (tpm2key, "parent", parent); + if (ret != ASN1_SUCCESS) + return grub_error (GRUB_ERR_READ_ERROR, "failed to retrieve parent"); + + return GRUB_ERR_NONE; +} + +static grub_err_t +tpm2key_get_octstring (asn1_node tpm2key, const char *name, void **data, grub_size_t *size) +{ + int ret; + + if (name == NULL || data == NULL || size == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid parameter(s)"); + + if (tpm2key == NULL) + return grub_error (GRUB_ERR_READ_ERROR, "invalid %s node", name); + + ret = asn1_allocate_and_read (tpm2key, name, data, size); + if (ret != ASN1_SUCCESS) + return grub_error (GRUB_ERR_READ_ERROR, "failed to retrieve %s", name); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_tpm2key_get_pubkey (asn1_node tpm2key, void **data, grub_size_t *size) +{ + return tpm2key_get_octstring (tpm2key, "pubkey", data, size); +} + +grub_err_t +grub_tpm2key_get_privkey (asn1_node tpm2key, void **data, grub_size_t *size) +{ + return tpm2key_get_octstring (tpm2key, "privkey", data, size); +} + +/* + * The maximum and minimum number of elements for 'policy' and 'authPolicy' sequences + * + * Although there is no limit for the number of sequences elements, we set the upper + * bound to 99 to make it easier to implement the code. + * + * Any 'policy' or 'authPolicy' contains more than 99 commands/policies would become + * extremely complex to manage so it is impractical to support such use case. + */ +#define TPM2KEY_ELEMENTS_MAX 99 +#define TPM2KEY_ELEMENTS_MIN 1 + +/* + * The string to fetch 'Policy' from 'authPolicy': + * authPolicy.?XX.Policy + */ +#define AUTHPOLICY_POL_MAX_STR "authPolicy.?XX.Policy" +#define AUTHPOLICY_POL_MAX (sizeof (AUTHPOLICY_POL_MAX_STR)) + +/* + * Expected strings for CommandCode and CommandPolicy: + * policy.?XX.CommandCode + * policy.?XX.CommandPolicy + * authPolicy.?XX.Policy.?YY.CommandCode + * authPolicy.?XX.Policy.?YY.CommandPolicy + */ +#define CMD_CODE_MAX_STR AUTHPOLICY_POL_MAX_STR".?YY.CommandCode" +#define CMD_POL_MAX_STR AUTHPOLICY_POL_MAX_STR".?YY.CommandPolicy" +#define CMD_CODE_MAX (sizeof (CMD_CODE_MAX_STR)) +#define CMD_POL_MAX (sizeof (CMD_POL_MAX_STR)) + +static int +tpm2key_get_policy_seq (asn1_node tpm2key, const char *prefix, + tpm2key_policy_t *policy_seq) +{ + tpm2key_policy_t tmp_seq = NULL; + tpm2key_policy_t policy = NULL; + int policy_n; + char cmd_code[CMD_CODE_MAX]; + char cmd_pol[CMD_POL_MAX]; + grub_size_t cmd_policy_len; + int i; + int ret; + + ret = asn1_number_of_elements (tpm2key, prefix, &policy_n); + if (ret != ASN1_SUCCESS) + return ret; + + /* + * Limit the number of policy commands to two digits (99) + * Although there is no upper bound for the number of policy commands, + * in practice, it takes one or two policy commands to unseal the key, + * so the 99 commands limit is more than enough. + */ + if (policy_n > TPM2KEY_ELEMENTS_MAX || policy_n < TPM2KEY_ELEMENTS_MIN) + return ASN1_VALUE_NOT_VALID; + + /* + * Iterate the policy commands backwards since grub_list_push() prepends + * the item into the list. + */ + for (i = policy_n; i >= 1; i--) { + policy = grub_zalloc (sizeof (struct tpm2key_policy)); + if (policy == NULL) + { + ret = ASN1_MEM_ALLOC_ERROR; + goto error; + } + grub_snprintf (cmd_code, CMD_CODE_MAX, "%s.?%d.CommandCode", prefix, i); + grub_snprintf (cmd_pol, CMD_POL_MAX, "%s.?%d.CommandPolicy", prefix, i); + + /* CommandCode [0] EXPLICIT INTEGER */ + ret = asn1_read_uint32 (tpm2key, cmd_code, &policy->cmd_code); + if (ret != ASN1_SUCCESS) + return ret; + + /* CommandPolicy [1] EXPLICIT OCTET STRING */ + ret = tpm2key_get_octstring (tpm2key, cmd_pol, &policy->cmd_policy, + &cmd_policy_len); + if (ret != ASN1_SUCCESS) + { + goto error; + } + else if (cmd_policy_len > GRUB_TPM2_BUFFER_CAPACITY) + { + /* + * CommandPolicy is the marshalled parameters for the TPM command so + * it should not be larger than the maximum TPM2 buffer. + */ + ret = ASN1_VALUE_NOT_VALID; + goto error; + } + policy->cmd_policy_len = (grub_uint16_t)cmd_policy_len; + + /* Prepend the policy command into the sequence */ + grub_list_push (GRUB_AS_LIST_P (&tmp_seq), GRUB_AS_LIST (policy)); + } + + *policy_seq = tmp_seq; + + return ASN1_SUCCESS; + + error: + if (policy != NULL) + { + grub_free (policy->cmd_policy); + grub_free (policy); + } + grub_tpm2key_free_policy_seq (tmp_seq); + + return ret; +} + +grub_err_t +grub_tpm2key_get_policy_seq (asn1_node tpm2key, tpm2key_policy_t *policy_seq) +{ + int ret; + + ret = tpm2key_get_policy_seq (tpm2key, "policy", policy_seq); + if (ret == ASN1_ELEMENT_NOT_FOUND) + { + /* "policy" is optional, so it may not be available */ + *policy_seq = NULL; + return GRUB_ERR_NONE; + } + else if (ret != ASN1_SUCCESS) + return grub_error (GRUB_ERR_READ_ERROR, "failed to retrieve policy"); + + return GRUB_ERR_NONE; +} + +void +grub_tpm2key_free_policy_seq (tpm2key_policy_t policy_seq) +{ + tpm2key_policy_t policy; + tpm2key_policy_t next; + + if (policy_seq == NULL) + return; + + FOR_LIST_ELEMENTS_SAFE (policy, next, policy_seq) + { + grub_free (policy->cmd_policy); + grub_free (policy); + } +} + +grub_err_t +grub_tpm2key_get_authpolicy_seq (asn1_node tpm2key, tpm2key_authpolicy_t *authpol_seq) +{ + tpm2key_authpolicy_t tmp_seq = NULL; + tpm2key_authpolicy_t authpol = NULL; + int authpol_n; + char authpol_pol[AUTHPOLICY_POL_MAX]; + int i; + int ret; + grub_err_t err; + + ret = asn1_number_of_elements (tpm2key, "authPolicy", &authpol_n); + if (ret == ASN1_ELEMENT_NOT_FOUND) + { + /* "authPolicy" is optional, so it may not be available */ + *authpol_seq = NULL; + return GRUB_ERR_NONE; + } + else if (ret != ASN1_SUCCESS) + return grub_error (GRUB_ERR_READ_ERROR, "failed to retrieve authPolicy"); + + /* Limit the number of authPolicy elements to two digits (99) */ + if (authpol_n > TPM2KEY_ELEMENTS_MAX || authpol_n < TPM2KEY_ELEMENTS_MIN) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "invalid number of authPolicy elements"); + + /* + * Iterate the authPolicy elements backwards since grub_list_push() prepends + * the item into the list. + */ + for (i = authpol_n; i >= 1; i--) { + authpol = grub_zalloc (sizeof (struct tpm2key_authpolicy)); + if (authpol == NULL) + { + err = grub_error (GRUB_ERR_OUT_OF_MEMORY, "failed to allocate memory for authPolicy"); + goto error; + } + grub_snprintf (authpol_pol, AUTHPOLICY_POL_MAX, "authPolicy.?%d.Policy", i); + + ret = tpm2key_get_policy_seq (tpm2key, authpol_pol, &authpol->policy_seq); + if (ret != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_READ_ERROR, "failed to retrieve policy from authPolicy"); + goto error; + } + + /* Prepend the authPolicy element into the sequence */ + grub_list_push (GRUB_AS_LIST_P (&tmp_seq), GRUB_AS_LIST (authpol)); + } + + *authpol_seq = tmp_seq; + + return GRUB_ERR_NONE; + + error: + if (authpol != NULL) + { + grub_tpm2key_free_policy_seq (authpol->policy_seq); + grub_free (authpol); + } + + grub_tpm2key_free_authpolicy_seq (tmp_seq); + + return err; +} + +void +grub_tpm2key_free_authpolicy_seq (tpm2key_authpolicy_t authpol_seq) +{ + tpm2key_authpolicy_t authpol; + tpm2key_authpolicy_t next; + + if (authpol_seq == NULL) + return; + + FOR_LIST_ELEMENTS_SAFE (authpol, next, authpol_seq) + { + grub_tpm2key_free_policy_seq (authpol->policy_seq); + grub_free (authpol); + } +} diff --git a/grub-core/commands/tpm2_key_protector/tpm2key.h b/grub-core/commands/tpm2_key_protector/tpm2key.h new file mode 100644 index 000000000..f749d62c2 --- /dev/null +++ b/grub-core/commands/tpm2_key_protector/tpm2key.h @@ -0,0 +1,87 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2023 SUSE LLC + * Copyright (C) 2024 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 GRUB_TPM2_TPM2KEY_HEADER +#define GRUB_TPM2_TPM2KEY_HEADER 1 + +#include +#include + +/* + * TPMPolicy ::= SEQUENCE { + * CommandCode [0] EXPLICIT INTEGER, + * CommandPolicy [1] EXPLICIT OCTET STRING + * } + */ +struct tpm2key_policy { + struct tpm2key_policy *next; + struct tpm2key_policy **prev; + grub_uint32_t cmd_code; + void *cmd_policy; + grub_uint16_t cmd_policy_len; +}; +typedef struct tpm2key_policy *tpm2key_policy_t; + +/* + * TPMAuthPolicy ::= SEQUENCE { + * Name [0] EXPLICIT UTF8String OPTIONAL, + * Policy [1] EXPLICIT SEQUENCE OF TPMPolicy + * } + * + * Name is not a necessary part to unseal the key. Ignore it. + */ +struct tpm2key_authpolicy { + struct tpm2key_authpolicy *next; + struct tpm2key_authpolicy **prev; + /* char *name; */ + tpm2key_policy_t policy_seq; +}; +typedef struct tpm2key_authpolicy *tpm2key_authpolicy_t; + +extern grub_err_t +grub_tpm2key_start_parsing (asn1_node *parsed_tpm2key, void *data, grub_size_t size); + +extern void +grub_tpm2key_end_parsing (asn1_node tpm2key); + +extern grub_err_t +grub_tpm2key_get_rsaparent (asn1_node tpm2key, grub_uint8_t *rsaparent); + +extern grub_err_t +grub_tpm2key_get_parent (asn1_node tpm2key, grub_uint32_t *parent); + +extern grub_err_t +grub_tpm2key_get_pubkey (asn1_node tpm2key, void **data, grub_size_t *size); + +extern grub_err_t +grub_tpm2key_get_privkey (asn1_node tpm2key, void **data, grub_size_t *size); + +extern grub_err_t +grub_tpm2key_get_policy_seq (asn1_node tpm2key, tpm2key_policy_t *policy_seq); + +extern void +grub_tpm2key_free_policy_seq (tpm2key_policy_t policy_seq); + +extern grub_err_t +grub_tpm2key_get_authpolicy_seq (asn1_node tpm2key, tpm2key_authpolicy_t *authpol_seq); + +extern void +grub_tpm2key_free_authpolicy_seq (tpm2key_authpolicy_t authpol_seq); + +#endif /* GRUB_TPM2_TPM2KEY_HEADER */ diff --git a/grub-core/commands/tpm2_key_protector/tpm2key_asn1_tab.c b/grub-core/commands/tpm2_key_protector/tpm2key_asn1_tab.c new file mode 100644 index 000000000..bebe108a3 --- /dev/null +++ b/grub-core/commands/tpm2_key_protector/tpm2key_asn1_tab.c @@ -0,0 +1,63 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2024 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 . + */ + +/* + * This file is generated by 'asn1Parser tpm2key.asn' and the '#include' + * headers are replaced with the ones in grub2. + * - 'grub/mm.h' for the definition of 'NULL' + * - 'libtasn1.h' for the definition of 'asn1_static_node' + */ + +#include +#include + +const asn1_static_node tpm2key_asn1_tab[] = { + { "TPM2KEY", 536875024, NULL }, + { NULL, 1073741836, NULL }, + { "TPMPolicy", 1610612741, NULL }, + { "CommandCode", 1610620931, NULL }, + { NULL, 2056, "0"}, + { "CommandPolicy", 536879111, NULL }, + { NULL, 2056, "1"}, + { "TPMAuthPolicy", 1610612741, NULL }, + { "Name", 1610637346, NULL }, + { NULL, 2056, "0"}, + { "Policy", 536879115, NULL }, + { NULL, 1073743880, "1"}, + { NULL, 2, "TPMPolicy"}, + { "TPMKey", 536870917, NULL }, + { "type", 1073741836, NULL }, + { "emptyAuth", 1610637316, NULL }, + { NULL, 2056, "0"}, + { "policy", 1610637323, NULL }, + { NULL, 1073743880, "1"}, + { NULL, 2, "TPMPolicy"}, + { "secret", 1610637319, NULL }, + { NULL, 2056, "2"}, + { "authPolicy", 1610637323, NULL }, + { NULL, 1073743880, "3"}, + { NULL, 2, "TPMAuthPolicy"}, + { "description", 1610637346, NULL }, + { NULL, 2056, "4"}, + { "rsaParent", 1610637316, NULL }, + { NULL, 2056, "5"}, + { "parent", 1073741827, NULL }, + { "pubkey", 1073741831, NULL }, + { "privkey", 7, NULL }, + { NULL, 0, NULL } +}; From ad0c52784a375cecaa8715d7deadcf5d65baf173 Mon Sep 17 00:00:00 2001 From: Hernan Gatta Date: Fri, 15 Nov 2024 15:34:52 +0800 Subject: [PATCH 233/402] cryptodisk: Support key protectors Add a new parameter to cryptomount to support the key protectors framework: -P. The parameter is used to automatically retrieve a key from specified key protectors. The parameter may be repeated to specify any number of key protectors. These are tried in order until one provides a usable key for any given disk. Signed-off-by: Hernan Gatta Signed-off-by: Michael Chang Signed-off-by: Gary Lin Reviewed-by: Glenn Washburn Reviewed-by: Stefan Berger Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- Makefile.util.def | 1 + docs/grub.texi | 12 +- grub-core/disk/cryptodisk.c | 261 ++++++++++++++++++++++++++---------- include/grub/cryptodisk.h | 16 +++ 4 files changed, 212 insertions(+), 78 deletions(-) diff --git a/Makefile.util.def b/Makefile.util.def index fe70cf9bd..fb82f59a0 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -40,6 +40,7 @@ library = { common = grub-core/disk/luks.c; common = grub-core/disk/luks2.c; common = grub-core/disk/geli.c; + common = grub-core/disk/key_protector.c; common = grub-core/disk/cryptodisk.c; common = grub-core/disk/AFSplitter.c; common = grub-core/lib/pbkdf2.c; diff --git a/docs/grub.texi b/docs/grub.texi index b03358bbf..9202380b8 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -6683,16 +6683,18 @@ Alias for @code{hashsum --hash crc32 arg @dots{}}. See command @command{hashsum} @node cryptomount @subsection cryptomount -@deffn Command cryptomount [ [@option{-p} password] | [@option{-k} keyfile [@option{-O} keyoffset] [@option{-S} keysize] ] ] [@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{-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). 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. 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). +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. +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). Argument @var{device} configures specific grub device (@pxref{Naming convention}); option @option{-u} @var{uuid} configures device with specified @var{uuid}; option @option{-a} configures all detected encrypted diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index 5e1eb2743..6f7394942 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -27,6 +27,7 @@ #include #include #include +#include #ifdef GRUB_UTIL #include @@ -45,7 +46,8 @@ enum OPTION_KEYFILE, OPTION_KEYFILE_OFFSET, OPTION_KEYFILE_SIZE, - OPTION_HEADER + OPTION_HEADER, + OPTION_PROTECTOR }; static const struct grub_arg_option options[] = @@ -59,6 +61,8 @@ static const struct grub_arg_option options[] = {"keyfile-offset", 'O', 0, N_("Key file offset (bytes)"), 0, ARG_TYPE_INT}, {"keyfile-size", 'S', 0, N_("Key file data size (bytes)"), 0, ARG_TYPE_INT}, {"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}, {0, 0, 0, 0, 0, 0} }; @@ -1062,6 +1066,7 @@ grub_cryptodisk_scan_device_real (const char *name, grub_err_t ret = GRUB_ERR_NONE; grub_cryptodisk_t dev; grub_cryptodisk_dev_t cr; + int i; struct cryptodisk_read_hook_ctx read_hook_data = {0}; int askpass = 0; char *part = NULL; @@ -1114,79 +1119,151 @@ grub_cryptodisk_scan_device_real (const char *name, goto error_no_close; if (!dev) continue; - - if (cargs->key_len) - { - ret = cr->recover_key (source, dev, cargs); - if (ret != GRUB_ERR_NONE) - goto error; - } - else - { - /* Get the passphrase from the user, if no key data. */ - unsigned long tries = 3; - const char *tries_env; - - askpass = 1; - cargs->key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE); - if (cargs->key_data == NULL) - goto error_no_close; - - tries_env = grub_env_get ("cryptodisk_passphrase_tries"); - if (tries_env != NULL && tries_env[0] != '\0') - { - unsigned long tries_env_val; - const char *p; - - tries_env_val = grub_strtoul (tries_env, &p, 0); - if (*p == '\0' && tries_env_val != ~0UL) - tries = tries_env_val; - else - grub_printf_ (N_("Invalid cryptodisk_passphrase_tries value `%s'. Defaulting to %lu.\n"), - tries_env, - tries); - } - - for (; tries > 0; tries--) - { - part = grub_partition_get_name (source->partition); - grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "), source->name, - source->partition != NULL ? "," : "", - part != NULL ? part : N_("UNKNOWN"), - dev->uuid); - grub_free (part); - - if (!grub_password_get ((char *) cargs->key_data, GRUB_CRYPTODISK_MAX_PASSPHRASE)) - { - grub_error (GRUB_ERR_BAD_ARGUMENT, "passphrase not supplied"); - goto error; - } - cargs->key_len = grub_strlen ((char *) cargs->key_data); - - ret = cr->recover_key (source, dev, cargs); - if (ret == GRUB_ERR_NONE) - break; - if (ret != GRUB_ERR_ACCESS_DENIED || tries == 1) - goto error; - grub_puts_ (N_("Invalid passphrase.")); - - /* - * Since recover_key() calls a function that returns grub_errno, - * a leftover error value from a previously rejected passphrase - * will trigger a phantom failure. We therefore clear it before - * trying a new passphrase. - */ - grub_errno = GRUB_ERR_NONE; - } - } - - ret = grub_cryptodisk_insert (dev, name, source); - if (ret != GRUB_ERR_NONE) - goto error; - - goto cleanup; + break; } - grub_error (GRUB_ERR_BAD_MODULE, "no cryptodisk module can handle this device"); + + if (dev == NULL) + { + grub_error (GRUB_ERR_BAD_MODULE, + "no cryptodisk module can handle this device"); + goto error_no_close; + } + + if (cargs->protectors) + { + for (i = 0; cargs->protectors[i]; i++) + { + if (cargs->key_cache[i].invalid) + continue; + + if (cargs->key_cache[i].key == NULL) + { + ret = grub_key_protector_recover_key (cargs->protectors[i], + &cargs->key_cache[i].key, + &cargs->key_cache[i].key_len); + if (ret != GRUB_ERR_NONE) + { + if (grub_errno) + { + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + } + + grub_dprintf ("cryptodisk", + "failed to recover a key from key protector " + "%s, will not try it again for any other " + "disks, if any, during this invocation of " + "cryptomount\n", + cargs->protectors[i]); + + cargs->key_cache[i].invalid = 1; + continue; + } + } + + cargs->key_data = cargs->key_cache[i].key; + cargs->key_len = cargs->key_cache[i].key_len; + + ret = cr->recover_key (source, dev, cargs); + if (ret != GRUB_ERR_NONE) + { + part = grub_partition_get_name (source->partition); + grub_dprintf ("cryptodisk", + "recovered a key from key protector %s but it " + "failed to unlock %s%s%s (%s)\n", + cargs->protectors[i], source->name, + source->partition != NULL ? "," : "", + part != NULL ? part : N_("UNKNOWN"), dev->uuid); + grub_free (part); + continue; + } + else + { + ret = grub_cryptodisk_insert (dev, name, source); + if (ret != GRUB_ERR_NONE) + goto error; + goto cleanup; + } + } + + part = grub_partition_get_name (source->partition); + grub_error (GRUB_ERR_ACCESS_DENIED, + N_("no key protector provided a usable key for %s%s%s (%s)"), + source->name, source->partition != NULL ? "," : "", + part != NULL ? part : N_("UNKNOWN"), dev->uuid); + grub_free (part); + goto error; + } + + if (cargs->key_len) + { + ret = cr->recover_key (source, dev, cargs); + if (ret != GRUB_ERR_NONE) + goto error; + } + else + { + /* Get the passphrase from the user, if no key data. */ + unsigned long tries = 3; + const char *tries_env; + + askpass = 1; + cargs->key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE); + if (cargs->key_data == NULL) + goto error_no_close; + + tries_env = grub_env_get ("cryptodisk_passphrase_tries"); + if (tries_env != NULL && tries_env[0] != '\0') + { + unsigned long tries_env_val; + const char *p; + + tries_env_val = grub_strtoul (tries_env, &p, 0); + if (*p == '\0' && tries_env_val != ~0UL) + tries = tries_env_val; + else + grub_printf_ (N_("Invalid cryptodisk_passphrase_tries value `%s'. Defaulting to %lu.\n"), + tries_env, + tries); + } + + for (; tries > 0; tries--) + { + part = grub_partition_get_name (source->partition); + grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "), source->name, + source->partition != NULL ? "," : "", + part != NULL ? part : N_("UNKNOWN"), + dev->uuid); + grub_free (part); + + if (!grub_password_get ((char *) cargs->key_data, GRUB_CRYPTODISK_MAX_PASSPHRASE)) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "passphrase not supplied"); + goto error; + } + cargs->key_len = grub_strlen ((char *) cargs->key_data); + + ret = cr->recover_key (source, dev, cargs); + if (ret == GRUB_ERR_NONE) + break; + if (ret != GRUB_ERR_ACCESS_DENIED || tries == 1) + goto error; + grub_puts_ (N_("Invalid passphrase.")); + + /* + * Since recover_key() calls a function that returns grub_errno, + * a leftover error value from a previously rejected passphrase + * will trigger a phantom failure. We therefore clear it before + * trying a new passphrase. + */ + grub_errno = GRUB_ERR_NONE; + } + } + + ret = grub_cryptodisk_insert (dev, name, source); + if (ret != GRUB_ERR_NONE) + goto error; + goto cleanup; error: @@ -1298,6 +1375,20 @@ grub_cryptodisk_scan_device (const char *name, return ret; } +static void +grub_cryptodisk_clear_key_cache (struct grub_cryptomount_args *cargs) +{ + int i; + + if (cargs->key_cache == NULL || cargs->protectors == NULL) + return; + + for (i = 0; cargs->protectors[i]; i++) + grub_free (cargs->key_cache[i].key); + + grub_free (cargs->key_cache); +} + static grub_err_t grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) { @@ -1310,6 +1401,14 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) if (grub_cryptodisk_list == NULL) return grub_error (GRUB_ERR_BAD_MODULE, "no cryptodisk modules loaded"); + if (state[OPTION_PASSWORD].set && state[OPTION_PROTECTOR].set) /* password and key protector */ + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "a password and a key protector cannot both be set"); + + if (state[OPTION_KEYFILE].set && state[OPTION_PROTECTOR].set) /* key file and key protector */ + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "a key file and a key protector cannot both be set"); + if (state[OPTION_PASSWORD].set) /* password */ { cargs.key_data = (grub_uint8_t *) state[OPTION_PASSWORD].arg; @@ -1402,6 +1501,15 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) return grub_errno; } + if (state[OPTION_PROTECTOR].set) /* key protector(s) */ + { + cargs.key_cache = grub_zalloc (state[OPTION_PROTECTOR].set * sizeof (*cargs.key_cache)); + if (cargs.key_cache == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "no memory for key protector key cache"); + cargs.protectors = state[OPTION_PROTECTOR].args; + } + if (state[OPTION_UUID].set) /* uuid */ { int found_uuid; @@ -1410,6 +1518,7 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) dev = grub_cryptodisk_get_by_uuid (args[0]); if (dev) { + grub_cryptodisk_clear_key_cache (&cargs); grub_dprintf ("cryptodisk", "already mounted as crypto%lu\n", dev->id); return GRUB_ERR_NONE; @@ -1418,6 +1527,7 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) cargs.check_boot = state[OPTION_BOOT].set; cargs.search_uuid = args[0]; found_uuid = grub_device_iterate (&grub_cryptodisk_scan_device, &cargs); + grub_cryptodisk_clear_key_cache (&cargs); if (found_uuid) return GRUB_ERR_NONE; @@ -1437,6 +1547,7 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) { cargs.check_boot = state[OPTION_BOOT].set; grub_device_iterate (&grub_cryptodisk_scan_device, &cargs); + grub_cryptodisk_clear_key_cache (&cargs); return GRUB_ERR_NONE; } else @@ -1460,6 +1571,7 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) disk = grub_disk_open (diskname); if (!disk) { + grub_cryptodisk_clear_key_cache (&cargs); if (disklast) *disklast = ')'; return grub_errno; @@ -1470,12 +1582,14 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) { grub_dprintf ("cryptodisk", "already mounted as crypto%lu\n", dev->id); grub_disk_close (disk); + grub_cryptodisk_clear_key_cache (&cargs); if (disklast) *disklast = ')'; return GRUB_ERR_NONE; } dev = grub_cryptodisk_scan_device_real (diskname, disk, &cargs); + grub_cryptodisk_clear_key_cache (&cargs); grub_disk_close (disk); if (disklast) @@ -1629,6 +1743,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 ...]]" " "), N_("Mount a crypto device."), options); grub_procfs_register ("luks_script", &luks_script); diff --git a/include/grub/cryptodisk.h b/include/grub/cryptodisk.h index d94df68b6..59b461e7a 100644 --- a/include/grub/cryptodisk.h +++ b/include/grub/cryptodisk.h @@ -70,6 +70,18 @@ typedef gcry_err_code_t (*grub_cryptodisk_rekey_func_t) (struct grub_cryptodisk *dev, grub_uint64_t zoneno); +struct grub_cryptomount_cached_key +{ + grub_uint8_t *key; + grub_size_t key_len; + + /* + * The key protector associated with this cache entry failed, so avoid it + * even if the cached entry (an instance of this structure) is empty. + */ + bool invalid; +}; + struct grub_cryptomount_args { /* scan: Flag to indicate that only bootable volumes should be decrypted */ @@ -81,6 +93,10 @@ struct grub_cryptomount_args /* recover_key: Length of key_data */ grub_size_t key_len; grub_file_t hdr_file; + /* recover_key: Names of the key protectors to use (NULL-terminated) */ + char **protectors; + /* recover_key: Key cache to avoid invoking the same key protector twice */ + struct grub_cryptomount_cached_key *key_cache; }; typedef struct grub_cryptomount_args *grub_cryptomount_args_t; From 5f6a2fd513e649e5750242f9e59d1294dfb0c855 Mon Sep 17 00:00:00 2001 From: Hernan Gatta Date: Fri, 15 Nov 2024 15:34:53 +0800 Subject: [PATCH 234/402] util/grub-protect: Add new tool To utilize the key protectors framework, there must be a way to protect full-disk encryption keys in the first place. The grub-protect tool includes support for the TPM2 key protector but other protectors that require setup ahead of time can be supported in the future. For the TPM2 key protector, the intended flow is for a user to have a LUKS 1 or LUKS 2-protected fully-encrypted disk. The user then creates a new LUKS key file, say by reading /dev/urandom into a file, and creates a new LUKS key slot for this key. Then, the user invokes the grub-protect tool to seal this key file to a set of PCRs using the system's TPM 2.0. The resulting sealed key file is stored in an unencrypted partition such as the EFI System Partition (ESP) so that GRUB may read it. The user also has to ensure the cryptomount command is included in GRUB's boot script and that it carries the requisite key protector (-P) parameter. Sample usage: $ dd if=/dev/urandom of=luks-key bs=1 count=32 $ sudo cryptsetup luksAddKey /dev/sdb1 luks-key --pbkdf=pbkdf2 --hash=sha512 To seal the key with TPM 2.0 Key File (recommended): $ sudo grub-protect --action=add \ --protector=tpm2 \ --tpm2-pcrs=0,2,4,7,9 \ --tpm2key \ --tpm2-keyfile=luks-key \ --tpm2-outfile=/boot/efi/efi/grub/sealed.tpm Or, to seal the key with the raw sealed key: $ sudo grub-protect --action=add \ --protector=tpm2 \ --tpm2-pcrs=0,2,4,7,9 \ --tpm2-keyfile=luks-key \ --tpm2-outfile=/boot/efi/efi/grub/sealed.key Then, in the boot script, for TPM 2.0 Key File: tpm2_key_protector_init --tpm2key=(hd0,gpt1)/efi/grub/sealed.tpm cryptomount -u -P tpm2 Or, for the raw sealed key: tpm2_key_protector_init --keyfile=(hd0,gpt1)/efi/grub/sealed.key --pcrs=0,2,4,7,9 cryptomount -u -P tpm2 The benefit of using TPM 2.0 Key File is that the PCR set is already written in the key file, so there is no need to specify PCRs when invoking tpm2_key_protector_init. Signed-off-by: Hernan Gatta Signed-off-by: Gary Lin Reviewed-by: Stefan Berger Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- .gitignore | 2 + Makefile.util.def | 26 + configure.ac | 30 + docs/man/grub-protect.h2m | 4 + util/grub-protect.c | 1407 +++++++++++++++++++++++++++++++++++++ 5 files changed, 1469 insertions(+) create mode 100644 docs/man/grub-protect.h2m create mode 100644 util/grub-protect.c diff --git a/.gitignore b/.gitignore index 4c1f91db8..2105d87c8 100644 --- a/.gitignore +++ b/.gitignore @@ -169,6 +169,8 @@ widthspec.bin /grub-ofpathname.exe /grub-probe /grub-probe.exe +/grub-protect +/grub-protect.exe /grub-reboot /grub-render-label /grub-render-label.exe diff --git a/Makefile.util.def b/Makefile.util.def index fb82f59a0..074c0aff7 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -208,6 +208,32 @@ program = { ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; +program = { + name = grub-protect; + mansection = 1; + + common = grub-core/kern/emu/argp_common.c; + common = grub-core/osdep/init.c; + common = grub-core/lib/tss2/buffer.c; + common = grub-core/lib/tss2/tss2_mu.c; + common = grub-core/lib/tss2/tpm2_cmd.c; + common = grub-core/commands/tpm2_key_protector/args.c; + common = grub-core/commands/tpm2_key_protector/tpm2key_asn1_tab.c; + common = util/grub-protect.c; + common = util/probe.c; + + 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 = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBTASN1)'; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; + + condition = COND_GRUB_PROTECT; +}; + program = { name = grub-mkrelpath; mansection = 1; diff --git a/configure.ac b/configure.ac index 458b8382b..ad1e7bea5 100644 --- a/configure.ac +++ b/configure.ac @@ -76,6 +76,7 @@ grub_TRANSFORM([grub-mkpasswd-pbkdf2]) grub_TRANSFORM([grub-mkrelpath]) grub_TRANSFORM([grub-mkrescue]) grub_TRANSFORM([grub-probe]) +grub_TRANSFORM([grub-protect]) grub_TRANSFORM([grub-reboot]) grub_TRANSFORM([grub-script-check]) grub_TRANSFORM([grub-set-default]) @@ -2068,6 +2069,29 @@ fi AC_SUBST([LIBZFS]) AC_SUBST([LIBNVPAIR]) +AC_ARG_ENABLE([grub-protect], + [AS_HELP_STRING([--enable-grub-protect], + [build and install the `grub-protect' utility (default=guessed)])]) +if test x"$enable_grub_protect" = xno ; then + grub_protect_excuse="explicitly disabled" +fi + +LIBTASN1= +if test x"$grub_protect_excuse" = x ; then + AC_CHECK_LIB([tasn1], [asn1_write_value], [LIBTASN1="-ltasn1"], [grub_protect_excuse="need libtasn1 library"]) +fi +AC_SUBST([LIBTASN1]) + +if test x"$enable_grub_protect" = xyes && test x"$grub_protect_excuse" != x ; then + AC_MSG_ERROR([grub-protect was explicitly requested but can't be compiled ($grub_protect_excuse)]) +fi +if test x"$grub_protect_excuse" = x ; then +enable_grub_protect=yes +else +enable_grub_protect=no +fi +AC_SUBST([enable_grub_protect]) + LIBS="" AC_SUBST([FONT_SOURCE]) @@ -2184,6 +2208,7 @@ AM_CONDITIONAL([COND_GRUB_EMU_SDL], [test x$enable_grub_emu_sdl = xyes]) AM_CONDITIONAL([COND_GRUB_EMU_PCI], [test x$enable_grub_emu_pci = xyes]) AM_CONDITIONAL([COND_GRUB_MKFONT], [test x$enable_grub_mkfont = xyes]) AM_CONDITIONAL([COND_GRUB_MOUNT], [test x$enable_grub_mount = xyes]) +AM_CONDITIONAL([COND_GRUB_PROTECT], [test x$enable_grub_protect = xyes]) AM_CONDITIONAL([COND_HAVE_FONT_SOURCE], [test x$FONT_SOURCE != x]) if test x$FONT_SOURCE != x ; then HAVE_FONT_SOURCE=1 @@ -2311,6 +2336,11 @@ echo grub-mount: Yes else echo grub-mount: No "($grub_mount_excuse)" fi +if [ x"$grub_protect_excuse" = x ]; then +echo grub-protect: Yes +else +echo grub-protect: No "($grub_protect_excuse)" +fi if [ x"$starfield_excuse" = x ]; then echo starfield theme: Yes echo With DejaVuSans font from $DJVU_FONT_SOURCE diff --git a/docs/man/grub-protect.h2m b/docs/man/grub-protect.h2m new file mode 100644 index 000000000..ecf1c9eab --- /dev/null +++ b/docs/man/grub-protect.h2m @@ -0,0 +1,4 @@ +[NAME] +grub-protect \- protect a disk key with a key protector +[DESCRIPTION] +grub-protect helps to protect a disk encryption key with a specified key protector. diff --git a/util/grub-protect.c b/util/grub-protect.c new file mode 100644 index 000000000..5b7e952f4 --- /dev/null +++ b/util/grub-protect.c @@ -0,0 +1,1407 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2023 SUSE LLC + * Copyright (C) 2024 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 + +#pragma GCC diagnostic ignored "-Wmissing-prototypes" +#pragma GCC diagnostic ignored "-Wmissing-declarations" +#include +#pragma GCC diagnostic error "-Wmissing-prototypes" +#pragma GCC diagnostic error "-Wmissing-declarations" + +#include "progname.h" + +/* Unprintable option keys for argp */ +typedef enum protect_opt +{ + /* General */ + PROTECT_OPT_ACTION = 'a', + PROTECT_OPT_PROTECTOR = 'p', + /* TPM2 */ + PROTECT_OPT_TPM2_DEVICE = 0x100, + PROTECT_OPT_TPM2_PCRS, + PROTECT_OPT_TPM2_ASYMMETRIC, + PROTECT_OPT_TPM2_BANK, + PROTECT_OPT_TPM2_SRK, + PROTECT_OPT_TPM2_KEYFILE, + PROTECT_OPT_TPM2_OUTFILE, + PROTECT_OPT_TPM2_EVICT, + PROTECT_OPT_TPM2_TPM2KEY +} protect_opt_t; + +/* Option flags to keep track of specified arguments */ +typedef enum protect_arg +{ + /* General */ + PROTECT_ARG_ACTION = 1 << 0, + PROTECT_ARG_PROTECTOR = 1 << 1, + /* TPM2 */ + PROTECT_ARG_TPM2_DEVICE = 1 << 2, + PROTECT_ARG_TPM2_PCRS = 1 << 3, + PROTECT_ARG_TPM2_ASYMMETRIC = 1 << 4, + PROTECT_ARG_TPM2_BANK = 1 << 5, + PROTECT_ARG_TPM2_SRK = 1 << 6, + PROTECT_ARG_TPM2_KEYFILE = 1 << 7, + PROTECT_ARG_TPM2_OUTFILE = 1 << 8, + PROTECT_ARG_TPM2_EVICT = 1 << 9, + PROTECT_ARG_TPM2_TPM2KEY = 1 << 10 +} protect_arg_t; + +typedef enum protect_protector +{ + PROTECT_TYPE_ERROR, + PROTECT_TYPE_TPM2 +} protect_protector_t; + +typedef enum protect_action +{ + PROTECT_ACTION_ERROR, + PROTECT_ACTION_ADD, + PROTECT_ACTION_REMOVE +} protect_action_t; + +typedef struct protect_args +{ + protect_arg_t args; + protect_action_t action; + protect_protector_t protector; + + const char *tpm2_device; + grub_uint8_t tpm2_pcrs[TPM_MAX_PCRS]; + grub_uint8_t tpm2_pcr_count; + grub_srk_type_t srk_type; + TPM_ALG_ID_t tpm2_bank; + TPM_HANDLE_t tpm2_srk; + const char *tpm2_keyfile; + const char *tpm2_outfile; + bool tpm2_evict; + bool tpm2_tpm2key; +} protect_args_t; + +static struct argp_option protect_options[] = + { + /* Top-level options */ + { + .name = "action", + .key = 'a', + .arg = "add|remove", + .flags = 0, + .doc = + N_("Add or remove a key protector to or from a key."), + .group = 0 + }, + { + .name = "protector", + .key = 'p', + .arg = "tpm2", + .flags = 0, + .doc = + N_("Set key protector to use (only tpm2 is currently supported)."), + .group = 0 + }, + /* TPM2 key protector options */ + { + .name = "tpm2-device", + .key = PROTECT_OPT_TPM2_DEVICE, + .arg = "FILE", + .flags = 0, + .doc = + N_("Set the path to the TPM2 device. (default: /dev/tpm0)"), + .group = 0 + }, + { + .name = "tpm2-pcrs", + .key = PROTECT_OPT_TPM2_PCRS, + .arg = "0[,1]...", + .flags = 0, + .doc = + N_("Set a comma-separated list of PCRs used to authorize key release " + "e.g., '7,11'. Please be aware that PCR 0~7 are used by the " + "firmware and the measurement result may change after a " + "firmware update (for baremetal systems) or a package " + "(OVMF/SLOF) update in the VM host. This may lead to " + "the failure of key unsealing. (default: 7)"), + .group = 0 + }, + { + .name = "tpm2-bank", + .key = PROTECT_OPT_TPM2_BANK, + .arg = "ALG", + .flags = 0, + .doc = + N_("Set the bank of PCRs used to authorize key release: " + "SHA1, SHA256, SHA384, or SHA512. (default: SHA256)"), + .group = 0 + }, + { + .name = "tpm2-keyfile", + .key = PROTECT_OPT_TPM2_KEYFILE, + .arg = "FILE", + .flags = 0, + .doc = + N_("Set the path to a file that contains the cleartext key to protect."), + .group = 0 + }, + { + .name = "tpm2-outfile", + .key = PROTECT_OPT_TPM2_OUTFILE, + .arg = "FILE", + .flags = 0, + .doc = + N_("Set the path to the file that will contain the key after sealing " + "(must be accessible to GRUB during boot)."), + .group = 0 + }, + { + .name = "tpm2-srk", + .key = PROTECT_OPT_TPM2_SRK, + .arg = "NUM", + .flags = 0, + .doc = + N_("Set the SRK handle if the SRK is to be made persistent."), + .group = 0 + }, + { + .name = "tpm2-asymmetric", + .key = PROTECT_OPT_TPM2_ASYMMETRIC, + .arg = "TYPE", + .flags = 0, + .doc = + N_("Set the type of SRK: RSA (RSA2048) and ECC (ECC_NIST_P256)." + "(default: ECC)"), + .group = 0 + }, + { + .name = "tpm2-evict", + .key = PROTECT_OPT_TPM2_EVICT, + .arg = NULL, + .flags = 0, + .doc = + N_("Evict a previously persisted SRK from the TPM, if any."), + .group = 0 + }, + { + .name = "tpm2key", + .key = PROTECT_OPT_TPM2_TPM2KEY, + .arg = NULL, + .flags = 0, + .doc = + N_("Use TPM 2.0 Key File format."), + .group = 0 + }, + /* End of list */ + { 0, 0, 0, 0, 0, 0 } + }; + +static int protector_tpm2_fd = -1; + +static grub_err_t +protect_read_file (const char *filepath, void **buffer, size_t *buffer_size) +{ + grub_err_t err; + FILE *f; + long len; + void *buf; + + f = fopen (filepath, "rb"); + if (f == NULL) + { + fprintf (stderr, N_("Could not open file: %s\n"), filepath); + return GRUB_ERR_FILE_NOT_FOUND; + } + + if (fseek (f, 0, SEEK_END)) + { + fprintf (stderr, N_("Could not seek file: %s\n"), filepath); + err = GRUB_ERR_FILE_READ_ERROR; + goto exit1; + } + + len = ftell (f); + if (len <= 0) + { + fprintf (stderr, N_("Could not get file length: %s\n"), filepath); + err = GRUB_ERR_FILE_READ_ERROR; + goto exit1; + } + + rewind (f); + + buf = grub_malloc (len); + if (buf == NULL) + { + fprintf (stderr, N_("Could not allocate memory for file: %s\n"), filepath); + err = GRUB_ERR_OUT_OF_MEMORY; + goto exit1; + } + + if (fread (buf, len, 1, f) != 1) + { + fprintf (stderr, N_("Could not read file: %s\n"), filepath); + err = GRUB_ERR_FILE_READ_ERROR; + goto exit2; + } + + *buffer = buf; + *buffer_size = len; + + buf = NULL; + err = GRUB_ERR_NONE; + + exit2: + grub_free (buf); + + exit1: + fclose (f); + + return err; +} + +static grub_err_t +protect_write_file (const char *filepath, void *buffer, size_t buffer_size) +{ + grub_err_t err; + FILE *f; + + f = fopen (filepath, "wb"); + if (f == NULL) + return GRUB_ERR_FILE_NOT_FOUND; + + if (fwrite (buffer, buffer_size, 1, f) != 1) + { + err = GRUB_ERR_WRITE_ERROR; + goto exit; + } + + err = GRUB_ERR_NONE; + + exit: + fclose (f); + + return err; +} + +grub_err_t +grub_tcg2_get_max_output_size (grub_size_t *size) +{ + if (size == NULL) + return GRUB_ERR_BAD_ARGUMENT; + + *size = GRUB_TPM2_BUFFER_CAPACITY; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_tcg2_submit_command (grub_size_t input_size, grub_uint8_t *input, + grub_size_t output_size, grub_uint8_t *output) +{ + if (write (protector_tpm2_fd, input, input_size) != input_size) + { + fprintf (stderr, N_("Could not send TPM command.\n")); + return GRUB_ERR_BAD_DEVICE; + } + + if (read (protector_tpm2_fd, output, output_size) < sizeof (TPM_RESPONSE_HEADER_t)) + { + fprintf (stderr, N_("Could not get TPM response.\n")); + return GRUB_ERR_BAD_DEVICE; + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +protect_tpm2_open_device (const char *dev_node) +{ + if (protector_tpm2_fd != -1) + return GRUB_ERR_NONE; + + protector_tpm2_fd = open (dev_node, O_RDWR); + if (protector_tpm2_fd == -1) + { + fprintf (stderr, N_("Could not open TPM device (%s).\n"), strerror (errno)); + return GRUB_ERR_FILE_NOT_FOUND; + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +protect_tpm2_close_device (void) +{ + int err; + + if (protector_tpm2_fd == -1) + return GRUB_ERR_NONE; + + err = close (protector_tpm2_fd); + if (err != GRUB_ERR_NONE) + { + fprintf (stderr, N_("Could not close TPM device (%s).\n"), strerror (errno)); + return GRUB_ERR_IO; + } + + protector_tpm2_fd = -1; + return GRUB_ERR_NONE; +} + +static grub_err_t +protect_tpm2_get_policy_digest (protect_args_t *args, TPM2B_DIGEST_t *digest) +{ + TPM_RC_t rc; + TPML_PCR_SELECTION_t pcr_sel = { + .count = 1, + .pcrSelections = { + { + .hash = args->tpm2_bank, + .sizeOfSelect = 3, + .pcrSelect = {0} + }, + } + }; + TPML_PCR_SELECTION_t pcr_sel_out = {0}; + TPML_DIGEST_t pcr_values = {0}; + TPM2B_DIGEST_t pcr_digest = {0}; + grub_size_t pcr_digest_len; + TPM2B_MAX_BUFFER_t pcr_concat = {0}; + grub_size_t pcr_concat_len; + grub_uint8_t *pcr_cursor; + TPM2B_NONCE_t nonce = {0}; + TPM2B_ENCRYPTED_SECRET_t salt = {0}; + TPMT_SYM_DEF_t symmetric = {0}; + TPMI_SH_AUTH_SESSION_t session = 0; + TPM2B_DIGEST_t policy_digest = {0}; + grub_uint8_t i; + grub_err_t err; + + /* PCR Read */ + for (i = 0; i < args->tpm2_pcr_count; i++) + TPMS_PCR_SELECTION_SelectPCR (&pcr_sel.pcrSelections[0], args->tpm2_pcrs[i]); + + rc = grub_tpm2_pcr_read (NULL, &pcr_sel, NULL, &pcr_sel_out, &pcr_values, NULL); + if (rc != TPM_RC_SUCCESS) + { + fprintf (stderr, "Failed to read PCRs (TPM2_PCR_Read: 0x%x).\n", rc); + return GRUB_ERR_BAD_DEVICE; + } + + if ((pcr_sel_out.count != pcr_sel.count) || + (pcr_sel.pcrSelections[0].sizeOfSelect != + pcr_sel_out.pcrSelections[0].sizeOfSelect)) + { + fprintf (stderr, N_("Could not read all the specified PCRs.\n")); + return GRUB_ERR_BAD_DEVICE; + } + + /* Compute PCR Digest */ + switch (args->tpm2_bank) + { + case TPM_ALG_SHA1: + pcr_digest_len = TPM_SHA1_DIGEST_SIZE; + break; + case TPM_ALG_SHA256: + pcr_digest_len = TPM_SHA256_DIGEST_SIZE; + break; + case TPM_ALG_SHA384: + pcr_digest_len = TPM_SHA384_DIGEST_SIZE; + break; + case TPM_ALG_SHA512: + pcr_digest_len = TPM_SHA512_DIGEST_SIZE; + break; + default: + return GRUB_ERR_BAD_ARGUMENT; + } + + pcr_concat_len = pcr_digest_len * args->tpm2_pcr_count; + if (pcr_concat_len > TPM_MAX_DIGEST_BUFFER) + { + fprintf (stderr, N_("PCR concatenation buffer not big enough.\n")); + return GRUB_ERR_OUT_OF_RANGE; + } + + pcr_cursor = pcr_concat.buffer; + for (i = 0; i < args->tpm2_pcr_count; i++) + { + if (pcr_values.digests[i].size != pcr_digest_len) + { + fprintf (stderr, + N_("Bad PCR value size: expected %llu bytes but got %u bytes.\n"), + (long long unsigned int)pcr_digest_len, pcr_values.digests[i].size); + return GRUB_ERR_BAD_ARGUMENT; + } + + grub_memcpy (pcr_cursor, pcr_values.digests[i].buffer, pcr_digest_len); + pcr_cursor += pcr_digest_len; + } + pcr_concat.size = pcr_concat_len; + + rc = grub_tpm2_hash (NULL, &pcr_concat, args->tpm2_bank, TPM_RH_NULL, &pcr_digest, NULL, NULL); + if (rc != TPM_RC_SUCCESS) + { + fprintf (stderr, "Failed to generate PCR digest (TPM2_Hash: 0x%x)\n", rc); + return GRUB_ERR_BAD_DEVICE; + } + + /* Start Trial Session */ + nonce.size = TPM_SHA256_DIGEST_SIZE; + symmetric.algorithm = TPM_ALG_NULL; + + rc = grub_tpm2_startauthsession (TPM_RH_NULL, TPM_RH_NULL, 0, &nonce, &salt, + TPM_SE_TRIAL, &symmetric, TPM_ALG_SHA256, + &session, NULL, 0); + if (rc != TPM_RC_SUCCESS) + { + fprintf (stderr, "Failed to start trial policy session (TPM2_StartAuthSession: 0x%x).\n", rc); + return GRUB_ERR_BAD_DEVICE; + } + + /* PCR Policy */ + rc = grub_tpm2_policypcr (session, NULL, &pcr_digest, &pcr_sel, NULL); + if (rc != TPM_RC_SUCCESS) + { + fprintf (stderr, "Failed to submit PCR policy (TPM2_PolicyPCR: 0x%x).\n", rc); + err = GRUB_ERR_BAD_DEVICE; + goto error; + } + + /* Retrieve Policy Digest */ + rc = grub_tpm2_policygetdigest (session, NULL, &policy_digest, NULL); + if (rc != TPM_RC_SUCCESS) + { + fprintf (stderr, "Failed to get policy digest (TPM2_PolicyGetDigest: 0x%x).\n", rc); + err = GRUB_ERR_BAD_DEVICE; + goto error; + } + + /* Epilogue */ + *digest = policy_digest; + err = GRUB_ERR_NONE; + + error: + grub_tpm2_flushcontext (session); + + return err; +} + +static grub_err_t +protect_tpm2_get_srk (protect_args_t *args, TPM_HANDLE_t *srk) +{ + TPM_RC_t rc; + TPM2B_PUBLIC_t public; + TPMS_AUTH_COMMAND_t authCommand = {0}; + TPM2B_SENSITIVE_CREATE_t inSensitive = {0}; + TPM2B_PUBLIC_t inPublic = {0}; + TPM2B_DATA_t outsideInfo = {0}; + TPML_PCR_SELECTION_t creationPcr = {0}; + TPM2B_PUBLIC_t outPublic = {0}; + TPM2B_CREATION_DATA_t creationData = {0}; + TPM2B_DIGEST_t creationHash = {0}; + TPMT_TK_CREATION_t creationTicket = {0}; + TPM2B_NAME_t srkName = {0}; + TPM_HANDLE_t srkHandle; + + if (args->tpm2_srk != 0) + { + /* Find SRK */ + rc = grub_tpm2_readpublic (args->tpm2_srk, NULL, &public); + if (rc == TPM_RC_SUCCESS) + { + printf ("Read SRK from 0x%x\n", args->tpm2_srk); + *srk = args->tpm2_srk; + return GRUB_ERR_NONE; + } + + /* The handle exists but its public area could not be read. */ + if ((rc & ~TPM_RC_N_MASK) != TPM_RC_HANDLE) + { + fprintf (stderr, "Failed to retrieve SRK from 0x%x (TPM2_ReadPublic: 0x%x).\n", args->tpm2_srk, rc); + return GRUB_ERR_BAD_DEVICE; + } + } + + /* Create SRK */ + authCommand.sessionHandle = TPM_RS_PW; + inPublic.publicArea.type = args->srk_type.type; + inPublic.publicArea.nameAlg = TPM_ALG_SHA256; + inPublic.publicArea.objectAttributes.restricted = 1; + inPublic.publicArea.objectAttributes.userWithAuth = 1; + inPublic.publicArea.objectAttributes.decrypt = 1; + inPublic.publicArea.objectAttributes.fixedTPM = 1; + inPublic.publicArea.objectAttributes.fixedParent = 1; + inPublic.publicArea.objectAttributes.sensitiveDataOrigin = 1; + inPublic.publicArea.objectAttributes.noDA = 1; + + switch (args->srk_type.type) + { + case TPM_ALG_RSA: + inPublic.publicArea.parameters.rsaDetail.symmetric.algorithm = TPM_ALG_AES; + inPublic.publicArea.parameters.rsaDetail.symmetric.keyBits.aes = 128; + inPublic.publicArea.parameters.rsaDetail.symmetric.mode.aes = TPM_ALG_CFB; + inPublic.publicArea.parameters.rsaDetail.scheme.scheme = TPM_ALG_NULL; + inPublic.publicArea.parameters.rsaDetail.keyBits = args->srk_type.detail.rsa_bits; + inPublic.publicArea.parameters.rsaDetail.exponent = 0; + break; + + case TPM_ALG_ECC: + inPublic.publicArea.parameters.eccDetail.symmetric.algorithm = TPM_ALG_AES; + inPublic.publicArea.parameters.eccDetail.symmetric.keyBits.aes = 128; + inPublic.publicArea.parameters.eccDetail.symmetric.mode.aes = TPM_ALG_CFB; + inPublic.publicArea.parameters.eccDetail.scheme.scheme = TPM_ALG_NULL; + inPublic.publicArea.parameters.eccDetail.curveID = args->srk_type.detail.ecc_curve; + inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL; + break; + + default: + return GRUB_ERR_BAD_ARGUMENT; + } + + rc = grub_tpm2_createprimary (TPM_RH_OWNER, &authCommand, &inSensitive, &inPublic, + &outsideInfo, &creationPcr, &srkHandle, &outPublic, + &creationData, &creationHash, &creationTicket, + &srkName, NULL); + if (rc != TPM_RC_SUCCESS) + { + fprintf (stderr, "Failed to create SRK (TPM2_CreatePrimary: 0x%x).\n", rc); + return GRUB_ERR_BAD_DEVICE; + } + + /* Persist SRK */ + if (args->tpm2_srk != 0) + { + rc = grub_tpm2_evictcontrol (TPM_RH_OWNER, srkHandle, &authCommand, args->tpm2_srk, NULL); + if (rc == TPM_RC_SUCCESS) + { + grub_tpm2_flushcontext (srkHandle); + srkHandle = args->tpm2_srk; + } + else + fprintf (stderr, + "Warning: Failed to persist SRK (0x%x) (TPM2_EvictControl: 0x%x).\n" + "Continuing anyway...\n", args->tpm2_srk, rc); + } + + /* Epilogue */ + *srk = srkHandle; + + return GRUB_ERR_NONE; +} + +static grub_err_t +protect_tpm2_seal (TPM2B_DIGEST_t *policyDigest, TPM_HANDLE_t srk, + grub_uint8_t *clearText, grub_size_t clearTextLength, + tpm2_sealed_key_t *sealed_key) +{ + TPM_RC_t rc; + TPMS_AUTH_COMMAND_t authCommand = {0}; + TPM2B_SENSITIVE_CREATE_t inSensitive = {0}; + TPM2B_PUBLIC_t inPublic = {0}; + TPM2B_DATA_t outsideInfo = {0}; + TPML_PCR_SELECTION_t pcr_sel = {0}; + TPM2B_PRIVATE_t outPrivate = {0}; + TPM2B_PUBLIC_t outPublic = {0}; + + /* Seal Data */ + authCommand.sessionHandle = TPM_RS_PW; + + inSensitive.sensitive.data.size = clearTextLength; + memcpy(inSensitive.sensitive.data.buffer, clearText, clearTextLength); + + inPublic.publicArea.type = TPM_ALG_KEYEDHASH; + inPublic.publicArea.nameAlg = TPM_ALG_SHA256; + inPublic.publicArea.parameters.keyedHashDetail.scheme.scheme = TPM_ALG_NULL; + inPublic.publicArea.authPolicy = *policyDigest; + + rc = grub_tpm2_create (srk, &authCommand, &inSensitive, &inPublic, &outsideInfo, + &pcr_sel, &outPrivate, &outPublic, NULL, NULL, NULL, NULL); + if (rc != TPM_RC_SUCCESS) + { + fprintf (stderr, "Failed to seal key (TPM2_Create: 0x%x).\n", rc); + return GRUB_ERR_BAD_DEVICE; + } + + /* Epilogue */ + sealed_key->public = outPublic; + sealed_key->private = outPrivate; + + return GRUB_ERR_NONE; +} + +extern asn1_static_node tpm2key_asn1_tab[]; + +/* id-sealedkey OID defined in TPM 2.0 Key Files Spec */ +#define TPM2KEY_SEALED_KEY_OID "2.23.133.10.1.5" + +static grub_err_t +protect_tpm2_export_tpm2key (const protect_args_t *args, + tpm2_sealed_key_t *sealed_key) +{ + const char *sealed_key_oid = TPM2KEY_SEALED_KEY_OID; + asn1_node asn1_def = NULL; + asn1_node tpm2key = NULL; + grub_uint32_t parent; + grub_uint32_t cmd_code; + struct grub_tpm2_buffer pol_buf; + TPML_PCR_SELECTION_t pcr_sel = { + .count = 1, + .pcrSelections = { + { + .hash = args->tpm2_bank, + .sizeOfSelect = 3, + .pcrSelect = {0} + }, + } + }; + struct grub_tpm2_buffer pub_buf; + struct grub_tpm2_buffer priv_buf; + void *der_buf = NULL; + int der_buf_size = 0; + int i; + int ret; + grub_err_t err; + + for (i = 0; i < args->tpm2_pcr_count; i++) + TPMS_PCR_SELECTION_SelectPCR (&pcr_sel.pcrSelections[0], args->tpm2_pcrs[i]); + + /* + * Prepare the parameters for TPM_CC_PolicyPCR: + * empty pcrDigest and the user selected PCRs + */ + grub_tpm2_buffer_init (&pol_buf); + grub_tpm2_buffer_pack_u16 (&pol_buf, 0); + grub_Tss2_MU_TPML_PCR_SELECTION_Marshal (&pol_buf, &pcr_sel); + + grub_tpm2_buffer_init (&pub_buf); + grub_Tss2_MU_TPM2B_PUBLIC_Marshal (&pub_buf, &sealed_key->public); + grub_tpm2_buffer_init (&priv_buf); + grub_Tss2_MU_TPM2B_Marshal (&priv_buf, sealed_key->private.size, + sealed_key->private.buffer); + if (pub_buf.error != 0 || priv_buf.error != 0) + return GRUB_ERR_BAD_ARGUMENT; + + ret = asn1_array2tree (tpm2key_asn1_tab, &asn1_def, NULL); + if (ret != ASN1_SUCCESS) + return GRUB_ERR_BAD_ARGUMENT; + + ret = asn1_create_element (asn1_def, "TPM2KEY.TPMKey" , &tpm2key); + if (ret != ASN1_SUCCESS) + return GRUB_ERR_BAD_ARGUMENT; + + /* Set 'type' to "sealed key" */ + ret = asn1_write_value (tpm2key, "type", sealed_key_oid, 1); + if (ret != ASN1_SUCCESS) + { + fprintf (stderr, "Failed to set 'type': 0x%u\n", ret); + err = GRUB_ERR_BAD_ARGUMENT; + goto error; + } + + /* Set 'emptyAuth' to TRUE */ + ret = asn1_write_value (tpm2key, "emptyAuth", "TRUE", 1); + if (ret != ASN1_SUCCESS) + { + fprintf (stderr, "Failed to set 'emptyAuth': 0x%x\n", ret); + err = GRUB_ERR_BAD_ARGUMENT; + goto error; + } + + /* Set 'policy' */ + ret = asn1_write_value (tpm2key, "policy", "NEW", 1); + if (ret != ASN1_SUCCESS) + { + fprintf (stderr, "Failed to set 'policy': 0x%x\n", ret); + err = GRUB_ERR_BAD_ARGUMENT; + goto error; + } + cmd_code = grub_cpu_to_be32 (TPM_CC_PolicyPCR); + ret = asn1_write_value (tpm2key, "policy.?LAST.CommandCode", &cmd_code, + sizeof (cmd_code)); + if (ret != ASN1_SUCCESS) + { + fprintf (stderr, "Failed to set 'policy CommandCode': 0x%x\n", ret); + err = GRUB_ERR_BAD_ARGUMENT; + goto error; + } + ret = asn1_write_value (tpm2key, "policy.?LAST.CommandPolicy", &pol_buf.data, + pol_buf.size); + if (ret != ASN1_SUCCESS) + { + fprintf (stderr, "Failed to set 'policy CommandPolicy': 0x%x\n", ret); + err = GRUB_ERR_BAD_ARGUMENT; + goto error; + } + + /* Remove 'secret' */ + ret = asn1_write_value (tpm2key, "secret", NULL, 0); + if (ret != ASN1_SUCCESS) + { + fprintf (stderr, "Failed to remove 'secret': 0x%x\n", ret); + err = GRUB_ERR_BAD_ARGUMENT; + goto error; + } + + /* Remove 'authPolicy' */ + ret = asn1_write_value (tpm2key, "authPolicy", NULL, 0); + if (ret != ASN1_SUCCESS) + { + fprintf (stderr, "Failed to remove 'authPolicy': 0x%x\n", ret); + err = GRUB_ERR_BAD_ARGUMENT; + goto error; + } + + /* Remove 'description' */ + ret = asn1_write_value (tpm2key, "description", NULL, 0); + if (ret != ASN1_SUCCESS) + { + fprintf (stderr, "Failed to remove 'description': 0x%x\n", ret); + err = GRUB_ERR_BAD_ARGUMENT; + goto error; + } + + /* + * Use the SRK handle as the parent handle if specified + * Otherwise, Use TPM_RH_OWNER as the default parent handle + */ + if (args->tpm2_srk != 0) + parent = grub_cpu_to_be32 (args->tpm2_srk); + else + parent = grub_cpu_to_be32 (TPM_RH_OWNER); + ret = asn1_write_value (tpm2key, "parent", &parent, sizeof (parent)); + if (ret != ASN1_SUCCESS) + { + fprintf (stderr, "Failed to set 'parent': 0x%x\n", ret); + err = GRUB_ERR_BAD_ARGUMENT; + goto error; + } + + /* + * Set 'rsaParent' to TRUE if the RSA SRK is specified and the SRK + * handle is not persistent. Otherwise, remove 'rsaParent'. + */ + if (args->tpm2_srk == 0 && args->srk_type.type == TPM_ALG_RSA) + ret = asn1_write_value (tpm2key, "rsaParent", "TRUE", 1); + else + ret = asn1_write_value (tpm2key, "rsaParent", NULL, 0); + + if (ret != ASN1_SUCCESS) + { + fprintf (stderr, "Failed to set 'rsaParent': 0x%x\n", ret); + err = GRUB_ERR_BAD_ARGUMENT; + goto error; + } + + /* Set the pubkey */ + ret = asn1_write_value (tpm2key, "pubkey", pub_buf.data, pub_buf.size); + if (ret != ASN1_SUCCESS) + { + fprintf (stderr, "Failed to set 'pubkey': 0x%x\n", ret); + err = GRUB_ERR_BAD_ARGUMENT; + goto error; + } + + /* Set the privkey */ + ret = asn1_write_value (tpm2key, "privkey", priv_buf.data, priv_buf.size); + if (ret != ASN1_SUCCESS) + { + fprintf (stderr, "Failed to set 'privkey': 0x%x\n", ret); + err = GRUB_ERR_BAD_ARGUMENT; + goto error; + } + + /* Create the DER binary */ + der_buf_size = 0; + ret = asn1_der_coding (tpm2key, "", NULL, &der_buf_size, NULL); + if (ret != ASN1_MEM_ERROR) + { + fprintf (stderr, "Failed to get DER size: 0x%x\n", ret); + err = GRUB_ERR_BAD_ARGUMENT; + goto error; + } + + der_buf = grub_malloc (der_buf_size); + if (der_buf == NULL) + { + fprintf (stderr, "Failed to allocate memory for DER encoding\n"); + err = GRUB_ERR_OUT_OF_MEMORY; + goto error; + } + + ret = asn1_der_coding (tpm2key, "", der_buf, &der_buf_size, NULL); + if (ret != ASN1_SUCCESS) + { + fprintf (stderr, "DER coding error: 0x%x\n", ret); + err = GRUB_ERR_BAD_ARGUMENT; + goto error; + } + + err = protect_write_file (args->tpm2_outfile, der_buf, der_buf_size); + if (err != GRUB_ERR_NONE) + fprintf (stderr, N_("Could not write tpm2key file (%s).\n"), strerror (errno)); + + error: + grub_free (der_buf); + + if (tpm2key) + asn1_delete_structure (&tpm2key); + + return err; +} + +static grub_err_t +protect_tpm2_export_sealed_key (const char *filepath, + tpm2_sealed_key_t *sealed_key) +{ + grub_err_t err; + struct grub_tpm2_buffer buf; + + grub_tpm2_buffer_init (&buf); + grub_Tss2_MU_TPM2B_PUBLIC_Marshal (&buf, &sealed_key->public); + grub_Tss2_MU_TPM2B_Marshal (&buf, sealed_key->private.size, + sealed_key->private.buffer); + if (buf.error != 0) + return GRUB_ERR_BAD_ARGUMENT; + + err = protect_write_file (filepath, buf.data, buf.size); + if (err != GRUB_ERR_NONE) + fprintf (stderr, N_("Could not write sealed key file (%s).\n"), strerror (errno)); + + return err; +} + +static grub_err_t +protect_tpm2_add (protect_args_t *args) +{ + grub_err_t err; + grub_uint8_t *key = NULL; + grub_size_t key_size; + TPM_HANDLE_t srk; + TPM2B_DIGEST_t policy_digest; + tpm2_sealed_key_t sealed_key; + + err = protect_tpm2_open_device (args->tpm2_device); + if (err != GRUB_ERR_NONE) + return err; + + err = protect_read_file (args->tpm2_keyfile, (void **)&key, &key_size); + if (err != GRUB_ERR_NONE) + goto exit1; + + if (key_size > TPM_MAX_SYM_DATA) + { + fprintf (stderr, N_("Input key size larger than %u bytes.\n"), TPM_MAX_SYM_DATA); + err = GRUB_ERR_OUT_OF_RANGE; + goto exit2; + } + + err = protect_tpm2_get_srk (args, &srk); + if (err != GRUB_ERR_NONE) + goto exit2; + + err = protect_tpm2_get_policy_digest (args, &policy_digest); + if (err != GRUB_ERR_NONE) + goto exit3; + + err = protect_tpm2_seal (&policy_digest, srk, key, key_size, &sealed_key); + if (err != GRUB_ERR_NONE) + goto exit3; + + if (args->tpm2_tpm2key != 0) + err = protect_tpm2_export_tpm2key (args, &sealed_key); + else + err = protect_tpm2_export_sealed_key (args->tpm2_outfile, &sealed_key); + if (err != GRUB_ERR_NONE) + goto exit3; + + exit3: + grub_tpm2_flushcontext (srk); + + exit2: + grub_free (key); + + exit1: + protect_tpm2_close_device (); + + return err; +} + +static grub_err_t +protect_tpm2_remove (protect_args_t *args) +{ + TPM_RC_t rc; + TPM2B_PUBLIC_t public; + TPMS_AUTH_COMMAND_t authCommand = {0}; + grub_err_t err; + + if (args->tpm2_evict == 0) + { + printf ("--tpm2-evict not specified, nothing to do.\n"); + return GRUB_ERR_NONE; + } + + err = protect_tpm2_open_device (args->tpm2_device); + if (err != GRUB_ERR_NONE) + return err; + + /* Find SRK */ + rc = grub_tpm2_readpublic (args->tpm2_srk, NULL, &public); + if (rc != TPM_RC_SUCCESS) + { + fprintf (stderr, "SRK with handle 0x%x not found.\n", args->tpm2_srk); + err = GRUB_ERR_BAD_ARGUMENT; + goto exit1; + } + + /* Evict SRK */ + authCommand.sessionHandle = TPM_RS_PW; + + rc = grub_tpm2_evictcontrol (TPM_RH_OWNER, args->tpm2_srk, &authCommand, args->tpm2_srk, NULL); + if (rc != TPM_RC_SUCCESS) + { + fprintf (stderr, "Failed to evict SRK with handle 0x%x (TPM2_EvictControl: 0x%x).\n", args->tpm2_srk, rc); + err = GRUB_ERR_BAD_DEVICE; + goto exit2; + } + + err = GRUB_ERR_NONE; + + exit2: + grub_tpm2_flushcontext (args->tpm2_srk); + + exit1: + protect_tpm2_close_device (); + + return GRUB_ERR_NONE; +} + +static grub_err_t +protect_tpm2_run (protect_args_t *args) +{ + switch (args->action) + { + case PROTECT_ACTION_ADD: + return protect_tpm2_add (args); + + case PROTECT_ACTION_REMOVE: + return protect_tpm2_remove (args); + + default: + return GRUB_ERR_BAD_ARGUMENT; + } +} + +static grub_err_t +protect_tpm2_args_verify (protect_args_t *args) +{ + if (args->tpm2_device == NULL) + args->tpm2_device = "/dev/tpm0"; + + switch (args->action) + { + case PROTECT_ACTION_ADD: + if (args->args & PROTECT_ARG_TPM2_EVICT) + { + fprintf (stderr, N_("--tpm2-evict is invalid when --action is 'add'.\n")); + return GRUB_ERR_BAD_ARGUMENT; + } + + if (args->tpm2_keyfile == NULL) + { + fprintf (stderr, N_("--tpm2-keyfile must be specified.\n")); + return GRUB_ERR_BAD_ARGUMENT; + } + + if (args->tpm2_outfile == NULL) + { + fprintf (stderr, N_("--tpm2-outfile must be specified.\n")); + return GRUB_ERR_BAD_ARGUMENT; + } + + if (args->tpm2_pcr_count == 0) + { + args->tpm2_pcrs[0] = 7; + args->tpm2_pcr_count = 1; + } + + if (args->srk_type.type == TPM_ALG_ERROR) + { + args->srk_type.type = TPM_ALG_ECC; + args->srk_type.detail.ecc_curve = TPM_ECC_NIST_P256; + } + + if (args->tpm2_bank == TPM_ALG_ERROR) + args->tpm2_bank = TPM_ALG_SHA256; + + break; + + case PROTECT_ACTION_REMOVE: + if (args->args & PROTECT_ARG_TPM2_ASYMMETRIC) + { + fprintf (stderr, N_("--tpm2-asymmetric is invalid when --action is 'remove'.\n")); + return GRUB_ERR_BAD_ARGUMENT; + } + + if (args->args & PROTECT_ARG_TPM2_BANK) + { + fprintf (stderr, N_("--tpm2-bank is invalid when --action is 'remove'.\n")); + return GRUB_ERR_BAD_ARGUMENT; + } + + if (args->args & PROTECT_ARG_TPM2_KEYFILE) + { + fprintf (stderr, N_("--tpm2-keyfile is invalid when --action is 'remove'.\n")); + return GRUB_ERR_BAD_ARGUMENT; + } + + if (args->args & PROTECT_ARG_TPM2_OUTFILE) + { + fprintf (stderr, N_("--tpm2-outfile is invalid when --action is 'remove'.\n")); + return GRUB_ERR_BAD_ARGUMENT; + } + + if (args->args & PROTECT_ARG_TPM2_PCRS) + { + fprintf (stderr, N_("--tpm2-pcrs is invalid when --action is 'remove'.\n")); + return GRUB_ERR_BAD_ARGUMENT; + } + + if (args->tpm2_srk == 0) + { + fprintf (stderr, N_("--tpm2-srk is not specified when --action is 'remove'.\n")); + return GRUB_ERR_BAD_ARGUMENT; + } + + break; + + default: + fprintf (stderr, N_("The TPM2 key protector only supports the following actions: add, remove.\n")); + return GRUB_ERR_BAD_ARGUMENT; + } + + return GRUB_ERR_NONE; +} + +static error_t +protect_argp_parser (int key, char *arg, struct argp_state *state) +{ + grub_err_t err; + protect_args_t *args = state->input; + + switch (key) + { + case PROTECT_OPT_ACTION: + if (args->args & PROTECT_ARG_ACTION) + { + fprintf (stderr, N_("--action|-a can only be specified once.\n")); + return EINVAL; + } + + if (grub_strcmp (arg, "add") == 0) + args->action = PROTECT_ACTION_ADD; + else if (grub_strcmp (arg, "remove") == 0) + args->action = PROTECT_ACTION_REMOVE; + else + { + fprintf (stderr, N_("'%s' is not a valid action.\n"), arg); + return EINVAL; + } + + args->args |= PROTECT_ARG_ACTION; + break; + + case PROTECT_OPT_PROTECTOR: + if (args->args & PROTECT_ARG_PROTECTOR) + { + fprintf (stderr, N_("--protector|-p can only be specified once.\n")); + return EINVAL; + } + + if (grub_strcmp (arg, "tpm2") == 0) + args->protector = PROTECT_TYPE_TPM2; + else + { + fprintf (stderr, N_("'%s' is not a valid protector.\n"), arg); + return EINVAL; + } + + args->args |= PROTECT_ARG_PROTECTOR; + break; + + case PROTECT_OPT_TPM2_DEVICE: + if (args->args & PROTECT_ARG_TPM2_DEVICE) + { + fprintf (stderr, N_("--tpm2-device can only be specified once.\n")); + return EINVAL; + } + + args->tpm2_device = xstrdup (arg); + args->args |= PROTECT_ARG_TPM2_DEVICE; + break; + + case PROTECT_OPT_TPM2_PCRS: + if (args->args & PROTECT_ARG_TPM2_PCRS) + { + fprintf (stderr, N_("--tpm2-pcrs can only be specified once.\n")); + return EINVAL; + } + + err = grub_tpm2_protector_parse_pcrs (arg, args->tpm2_pcrs, + &args->tpm2_pcr_count); + if (err != GRUB_ERR_NONE) + { + if (grub_errno != GRUB_ERR_NONE) + grub_print_error (); + return EINVAL; + } + + args->args |= PROTECT_ARG_TPM2_PCRS; + break; + + case PROTECT_OPT_TPM2_SRK: + if (args->args & PROTECT_ARG_TPM2_SRK) + { + fprintf (stderr, N_("--tpm2-srk can only be specified once.\n")); + return EINVAL; + } + + err = grub_tpm2_protector_parse_tpm_handle (arg, &args->tpm2_srk); + if (err != GRUB_ERR_NONE) + { + if (grub_errno != GRUB_ERR_NONE) + grub_print_error (); + return EINVAL; + } + + args->args |= PROTECT_ARG_TPM2_SRK; + break; + + case PROTECT_OPT_TPM2_ASYMMETRIC: + if (args->args & PROTECT_ARG_TPM2_ASYMMETRIC) + { + fprintf (stderr, N_("--tpm2-asymmetric can only be specified once.\n")); + return EINVAL; + } + + err = grub_tpm2_protector_parse_asymmetric (arg, &args->srk_type); + if (err != GRUB_ERR_NONE) + { + if (grub_errno != GRUB_ERR_NONE) + grub_print_error (); + return EINVAL; + } + + args->args |= PROTECT_ARG_TPM2_ASYMMETRIC; + break; + + case PROTECT_OPT_TPM2_BANK: + if (args->args & PROTECT_ARG_TPM2_BANK) + { + fprintf (stderr, N_("--tpm2-bank can only be specified once.\n")); + return EINVAL; + } + + err = grub_tpm2_protector_parse_bank (arg, &args->tpm2_bank); + if (err != GRUB_ERR_NONE) + { + if (grub_errno != GRUB_ERR_NONE) + grub_print_error (); + return EINVAL; + } + + args->args |= PROTECT_ARG_TPM2_BANK; + break; + + case PROTECT_OPT_TPM2_KEYFILE: + if (args->args & PROTECT_ARG_TPM2_KEYFILE) + { + fprintf (stderr, N_("--tpm2-keyfile can only be specified once.\n")); + return EINVAL; + } + + args->tpm2_keyfile = xstrdup(arg); + args->args |= PROTECT_ARG_TPM2_KEYFILE; + break; + + case PROTECT_OPT_TPM2_OUTFILE: + if (args->args & PROTECT_ARG_TPM2_OUTFILE) + { + fprintf (stderr, N_("--tpm2-outfile can only be specified once.\n")); + return EINVAL; + } + + args->tpm2_outfile = xstrdup(arg); + args->args |= PROTECT_ARG_TPM2_OUTFILE; + break; + + case PROTECT_OPT_TPM2_EVICT: + if (args->args & PROTECT_ARG_TPM2_EVICT) + { + fprintf (stderr, N_("--tpm2-evict can only be specified once.\n")); + return EINVAL; + } + + args->tpm2_evict = 1; + args->args |= PROTECT_ARG_TPM2_EVICT; + break; + + case PROTECT_OPT_TPM2_TPM2KEY: + if (args->args & PROTECT_ARG_TPM2_TPM2KEY) + { + fprintf (stderr, N_("--tpm2-tpm2key can only be specified once.\n")); + return EINVAL; + } + + args->tpm2_tpm2key = 1; + args->args |= PROTECT_ARG_TPM2_TPM2KEY; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +static grub_err_t +protect_args_verify (protect_args_t *args) +{ + if (args->action == PROTECT_ACTION_ERROR) + { + fprintf (stderr, N_("--action is mandatory.\n")); + return GRUB_ERR_BAD_ARGUMENT; + } + + /* + * At the moment, the only configurable key protector is the TPM2 one, so it + * is the only key protector supported by this tool. + */ + if (args->protector != PROTECT_TYPE_TPM2) + { + fprintf (stderr, N_("--protector is mandatory and only 'tpm2' is currently supported.\n")); + return GRUB_ERR_BAD_ARGUMENT; + } + + switch (args->protector) + { + case PROTECT_TYPE_TPM2: + return protect_tpm2_args_verify (args); + default: + return GRUB_ERR_BAD_ARGUMENT; + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +protect_dispatch (protect_args_t *args) +{ + switch (args->protector) + { + case PROTECT_TYPE_TPM2: + return protect_tpm2_run (args); + default: + return GRUB_ERR_BAD_ARGUMENT; + } +} + +static void +protect_init (int *argc, char **argv[]) +{ + grub_util_host_init (argc, argv); + + grub_util_biosdisk_init (NULL); + + grub_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 (); +} + +static void +protect_fini (void) +{ + grub_fini_all (); + grub_util_biosdisk_fini (); +} + +static struct argp protect_argp = +{ + .options = protect_options, + .parser = protect_argp_parser, + .args_doc = NULL, + .doc = + N_("Protect a cleartext key using a GRUB key protector that can retrieve " + "the key during boot to unlock fully-encrypted disks automatically."), + .children = NULL, + .help_filter = NULL, + .argp_domain = NULL +}; + +int +main (int argc, char *argv[]) +{ + grub_err_t err; + protect_args_t args = {0}; + + if (argp_parse (&protect_argp, argc, argv, 0, 0, &args) != 0) + { + fprintf (stderr, N_("Could not parse arguments.\n")); + return EXIT_FAILURE; + } + + protect_init (&argc, &argv); + + err = protect_args_verify (&args); + if (err != GRUB_ERR_NONE) + goto exit; + + err = protect_dispatch (&args); + + exit: + protect_fini (); + + if (err != GRUB_ERR_NONE) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} From 550ada7d67aa9690a3ba94d5f8d1b11515f5a502 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:34:54 +0800 Subject: [PATCH 235/402] tpm2_key_protector: Support authorized policy This commit handles the TPM2_PolicyAuthorize command from the key file in TPM 2.0 Key File format. TPM2_PolicyAuthorize is the essential command to support authorized policy which allows the users to sign TPM policies with their own keys. Per TPM 2.0 Key File [1], CommandPolicy for TPM2_PolicyAuthorize comprises "TPM2B_PUBLIC pubkey", "TPM2B_DIGEST policy_ref", and "TPMT_SIGNATURE signature". To verify the signature, the current policy digest is hashed with the hash algorithm written in "signature", and then "signature" is verified with the hashed policy digest and "pubkey". Once TPM accepts "signature", TPM2_PolicyAuthorize is invoked to authorize the signed policy. To create the key file with authorized policy, here are the pcr-oracle [2] commands: # Generate the RSA key and create the authorized policy file $ pcr-oracle \ --rsa-generate-key \ --private-key policy-key.pem \ --auth authorized.policy \ create-authorized-policy 0,2,4,7,9 # Seal the secret with the authorized policy $ pcr-oracle \ --key-format tpm2.0 \ --auth authorized.policy \ --input disk-secret.txt \ --output sealed.key \ seal-secret # Sign the predicted PCR policy $ pcr-oracle \ --key-format tpm2.0 \ --private-key policy-key.pem \ --from eventlog \ --stop-event "grub-file=grub.cfg" \ --after \ --input sealed.key \ --output /boot/efi/efi/grub/sealed.tpm \ sign 0,2,4,7,9 Then specify the key file and the key protector to grub.cfg in the EFI system partition: tpm2_key_protector_init -a RSA --tpm2key=(hd0,gpt1)/efi/grub/sealed.tpm cryptomount -u -P tpm2 For any change in the boot components, just run the "sign" command again to update the signature in sealed.tpm, and TPM can unseal the key file with the updated PCR policy. [1] https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html [2] https://github.com/okirch/pcr-oracle Signed-off-by: Gary Lin Reviewed-by: Stefan Berger Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- .../commands/tpm2_key_protector/module.c | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/grub-core/commands/tpm2_key_protector/module.c b/grub-core/commands/tpm2_key_protector/module.c index 70d4d0df7..6b4b5d460 100644 --- a/grub-core/commands/tpm2_key_protector/module.c +++ b/grub-core/commands/tpm2_key_protector/module.c @@ -618,6 +618,73 @@ tpm2_protector_policypcr (TPMI_SH_AUTH_SESSION_t session, struct grub_tpm2_buffe return GRUB_ERR_NONE; } +static grub_err_t +tpm2_protector_policyauthorize (TPMI_SH_AUTH_SESSION_t session, struct grub_tpm2_buffer *cmd_buf) +{ + TPM2B_PUBLIC_t pubkey; + TPM2B_DIGEST_t policy_ref; + TPMT_SIGNATURE_t signature; + TPM2B_DIGEST_t pcr_policy; + TPM2B_DIGEST_t pcr_policy_hash; + TPMI_ALG_HASH_t sig_hash; + TPMT_TK_VERIFIED_t verification_ticket; + TPM_HANDLE_t pubkey_handle = 0; + TPM2B_NAME_t pubname; + TPM_RC_t rc; + grub_err_t err; + + grub_Tss2_MU_TPM2B_PUBLIC_Unmarshal (cmd_buf, &pubkey); + grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (cmd_buf, &policy_ref); + grub_Tss2_MU_TPMT_SIGNATURE_Unmarshal (cmd_buf, &signature); + if (cmd_buf->error != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to unmarshal the buffer for TPM2_PolicyAuthorize"); + + /* Retrieve Policy Digest */ + rc = grub_tpm2_policygetdigest (session, NULL, &pcr_policy, NULL); + if (rc != TPM_RC_SUCCESS) + return grub_error (GRUB_ERR_BAD_DEVICE, "failed to get policy digest (TPM2_PolicyGetDigest: 0x%x).", rc); + + /* Calculate the digest of the polcy for VerifySignature */ + sig_hash = TPMT_SIGNATURE_get_hash_alg (&signature); + if (sig_hash == TPM_ALG_NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to get the hash algorithm of the signature"); + + rc = grub_tpm2_hash (NULL, (TPM2B_MAX_BUFFER_t *) &pcr_policy, sig_hash, + TPM_RH_NULL, &pcr_policy_hash, NULL, NULL); + if (rc != TPM_RC_SUCCESS) + return grub_error (GRUB_ERR_BAD_DEVICE, "failed to create PCR policy hash (TPM2_Hash: 0x%x)", rc); + + /* Load the public key */ + rc = grub_tpm2_loadexternal (NULL, NULL, &pubkey, TPM_RH_OWNER, &pubkey_handle, &pubname, NULL); + if (rc != TPM_RC_SUCCESS) + return grub_error (GRUB_ERR_BAD_DEVICE, "failed to load public key (TPM2_LoadExternal: 0x%x)", rc); + + /* Verify the signature against the public key and the policy digest */ + rc = grub_tpm2_verifysignature (pubkey_handle, NULL, &pcr_policy_hash, &signature, + &verification_ticket, NULL); + if (rc != TPM_RC_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_DEVICE, "failed to verify signature (TPM2_VerifySignature: 0x%x)", rc); + goto error; + } + + /* Authorize the signed policy with the public key and the verification ticket */ + rc = grub_tpm2_policyauthorize (session, NULL, &pcr_policy, &policy_ref, &pubname, + &verification_ticket, NULL); + if (rc != TPM_RC_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_DEVICE, "failed to authorize PCR policy (TPM2_PolicyAuthorize: 0x%x)", rc); + goto error; + } + + err = GRUB_ERR_NONE; + + error: + grub_tpm2_flushcontext (pubkey_handle); + + return err; +} + static grub_err_t tpm2_protector_enforce_policy (tpm2key_policy_t policy, TPMI_SH_AUTH_SESSION_t session) { @@ -636,6 +703,9 @@ tpm2_protector_enforce_policy (tpm2key_policy_t policy, TPMI_SH_AUTH_SESSION_t s case TPM_CC_PolicyPCR: err = tpm2_protector_policypcr (session, &buf); break; + case TPM_CC_PolicyAuthorize: + err = tpm2_protector_policyauthorize (session, &buf); + break; default: return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown TPM Command: 0x%x", policy->cmd_code); } From fba3a474e00ca10d6ec29ed4667de8911e4068e5 Mon Sep 17 00:00:00 2001 From: Patrick Colp Date: Fri, 15 Nov 2024 15:34:55 +0800 Subject: [PATCH 236/402] tpm2_key_protector: Implement NV index Currently with the TPM2 protector, only SRK mode is supported and NV index support is just a stub. Implement the NV index option. Note: This only extends support on the unseal path. grub-protect has not been updated. tpm2-tools can be used to insert a key into the NV index. An example of inserting a key using tpm2-tools: # Get random key. tpm2_getrandom 32 > key.dat # Create primary object. tpm2_createprimary -C o -g sha256 -G ecc -c primary.ctx # Create policy object. `pcrs.dat` contains the PCR values to seal against. tpm2_startauthsession -S session.dat tpm2_policypcr -S session.dat -l sha256:7,11 -f pcrs.dat -L policy.dat tpm2_flushcontext session.dat # Seal key into TPM. cat key.dat | tpm2_create -C primary.ctx -u key.pub -r key.priv -L policy.dat -i- tpm2_load -C primary.ctx -u key.pub -r key.priv -n sealing.name -c sealing.ctx tpm2_evictcontrol -C o -c sealing.ctx 0x81000000 Then to unseal the key in GRUB, add this to grub.cfg: tpm2_key_protector_init --mode=nv --nvindex=0x81000000 --pcrs=7,11 cryptomount -u --protector tpm2 Signed-off-by: Patrick Colp Signed-off-by: Gary Lin Reviewed-by: Stefan Berger Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- .../commands/tpm2_key_protector/module.c | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/grub-core/commands/tpm2_key_protector/module.c b/grub-core/commands/tpm2_key_protector/module.c index 6b4b5d460..74e79a545 100644 --- a/grub-core/commands/tpm2_key_protector/module.c +++ b/grub-core/commands/tpm2_key_protector/module.c @@ -973,11 +973,26 @@ tpm2_protector_srk_recover (const tpm2_protector_context_t *ctx, } static grub_err_t -tpm2_protector_nv_recover (const tpm2_protector_context_t *ctx __attribute__ ((unused)), - grub_uint8_t **key __attribute__ ((unused)), - grub_size_t *key_size __attribute__ ((unused))) +tpm2_protector_nv_recover (const tpm2_protector_context_t *ctx, + grub_uint8_t **key, grub_size_t *key_size) { - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "NV Index mode is not implemented yet"); + TPM_HANDLE_t sealed_handle = ctx->nv; + tpm2key_policy_t policy_seq = NULL; + grub_err_t err; + + /* Create a basic policy sequence based on the given PCR selection */ + err = tpm2_protector_simple_policy_seq (ctx, &policy_seq); + if (err != GRUB_ERR_NONE) + goto exit; + + err = tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size); + + exit: + grub_tpm2_flushcontext (sealed_handle); + + grub_tpm2key_free_policy_seq (policy_seq); + + return err; } static grub_err_t From 6abf8af3c54abc04c4ec71c75d10fcfbc190e181 Mon Sep 17 00:00:00 2001 From: Patrick Colp Date: Fri, 15 Nov 2024 15:34:56 +0800 Subject: [PATCH 237/402] cryptodisk: Fallback to passphrase If a protector is specified, but it fails to unlock the disk, fall back to asking for the passphrase. Before requesting the passphrase, the error from the key protector(s) has to be cleared, or the later code, e.g., LUKS code, may stop as grub_errno is set. This commit prints error from the key protector(s) and sets grub_errno to GRUB_ERR_NONE to have a fresh start. Signed-off-by: Patrick Colp Signed-off-by: Gary Lin Reviewed-by: Stefan Berger Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- grub-core/disk/cryptodisk.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index 6f7394942..4219f1fb6 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -1167,6 +1167,10 @@ grub_cryptodisk_scan_device_real (const char *name, ret = cr->recover_key (source, dev, cargs); if (ret != GRUB_ERR_NONE) { + /* Reset key data to trigger the passphrase prompt later */ + cargs->key_data = NULL; + cargs->key_len = 0; + part = grub_partition_get_name (source->partition); grub_dprintf ("cryptodisk", "recovered a key from key protector %s but it " @@ -1192,7 +1196,6 @@ grub_cryptodisk_scan_device_real (const char *name, source->name, source->partition != NULL ? "," : "", part != NULL ? part : N_("UNKNOWN"), dev->uuid); grub_free (part); - goto error; } if (cargs->key_len) @@ -1207,6 +1210,24 @@ grub_cryptodisk_scan_device_real (const char *name, unsigned long tries = 3; const char *tries_env; + /* + * Print the error from key protectors and clear grub_errno. + * + * Since '--protector' cannot coexist with '--password' and + * '--key-file', in case key protectors fail, only + * "cargs->key_len == 0" is expected, so cryptomount falls back + * here to request the passphrase. + * + * To avoid the error from key protectors stops the further code, + * print the error to notify the user why key protectors fail and + * clear grub_errno to have a fresh start. + */ + if (grub_errno != GRUB_ERR_NONE) + { + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + } + askpass = 1; cargs->key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE); if (cargs->key_data == NULL) From b35480b48e6f9506d8b7ad8a3b5206d29c24ea95 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:34:57 +0800 Subject: [PATCH 238/402] cryptodisk: Wipe out the cached keys from protectors An attacker may insert a malicious disk with the same crypto UUID and trick GRUB to mount the fake root. Even though the key from the key protector fails to unlock the fake root, it's not wiped out cleanly so the attacker could dump the memory to retrieve the secret key. To defend such attack, wipe out the cached key when we don't need it. Cc: Fabian Vogt Signed-off-by: Gary Lin Reviewed-by: Stefan Berger Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- grub-core/disk/cryptodisk.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index 4219f1fb6..5fc41979e 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -1405,7 +1405,11 @@ grub_cryptodisk_clear_key_cache (struct grub_cryptomount_args *cargs) return; for (i = 0; cargs->protectors[i]; i++) - grub_free (cargs->key_cache[i].key); + { + if (cargs->key_cache[i].key) + grub_memset (cargs->key_cache[i].key, 0, cargs->key_cache[i].key_len); + grub_free (cargs->key_cache[i].key); + } grub_free (cargs->key_cache); } From 135e0bc8863965eabc70f3c539c39135e0a00791 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:34:58 +0800 Subject: [PATCH 239/402] diskfilter: Look up cryptodisk devices first When using disk auto-unlocking with TPM 2.0, the typical grub.cfg may look like this: tpm2_key_protector_init --tpm2key=(hd0,gpt1)/boot/grub/sealed.tpm cryptomount -u -P tpm2 search --fs-uuid --set=root Since the disk search order is based on the order of module loading, the attacker could insert a malicious disk with the same FS-UUID root to trick GRUB to boot into the malicious root and further dump memory to steal the unsealed key. Do defend against such an attack, we can specify the hint provided by "grub-probe" to search the encrypted partition first: search --fs-uuid --set=root --hint='cryptouuid/' However, for LVM on an encrypted partition, the search hint provided by "grub-probe" is: --hint='lvmid//' It doesn't guarantee to look up the logical volume from the encrypted partition, so the attacker may have the chance to fool GRUB to boot into the malicious disk. To minimize the attack surface, this commit tweaks the disk device search in diskfilter to look up cryptodisk devices first and then others, so that the auto-unlocked disk will be found first, not the attacker's disk. Cc: Fabian Vogt Signed-off-by: Gary Lin Reviewed-by: Stefan Berger Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- grub-core/disk/diskfilter.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/grub-core/disk/diskfilter.c b/grub-core/disk/diskfilter.c index 21e239511..606195c26 100644 --- a/grub-core/disk/diskfilter.c +++ b/grub-core/disk/diskfilter.c @@ -226,15 +226,28 @@ scan_devices (const char *arname) int need_rescan; for (pull = 0; pull < GRUB_DISK_PULL_MAX; pull++) - for (p = grub_disk_dev_list; p; p = p->next) - if (p->id != GRUB_DISK_DEVICE_DISKFILTER_ID - && p->disk_iterate) - { - if ((p->disk_iterate) (scan_disk_hook, NULL, pull)) - return; - if (arname && is_lv_readable (find_lv (arname), 1)) - return; - } + { + /* look up the crytodisk devices first */ + for (p = grub_disk_dev_list; p; p = p->next) + if (p->id == GRUB_DISK_DEVICE_CRYPTODISK_ID && p->disk_iterate) + { + if ((p->disk_iterate) (scan_disk_hook, NULL, pull)) + return; + if (arname && is_lv_readable (find_lv (arname), 1)) + return; + break; + } + + /* check the devices other than crytodisk */ + for (p = grub_disk_dev_list; p; p = p->next) + if (p->id != GRUB_DISK_DEVICE_DISKFILTER_ID && p->disk_iterate) + { + if ((p->disk_iterate) (scan_disk_hook, NULL, pull)) + return; + if (arname && is_lv_readable (find_lv (arname), 1)) + return; + } + } scan_depth = 0; need_rescan = 1; From 76a2bcb99754ee5b4159c35f66042e392139b815 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:34:59 +0800 Subject: [PATCH 240/402] tpm2_key_protector: Add grub-emu support As a preparation to test tpm2_key_protector with grub-emu, the new option, --tpm-device, is introduced to specify the TPM device for grub-emu so that grub-emu can access an emulated TPM device from the host. Since grub-emu can directly access the device on host, it's easy to implement the essential TCG2 command submission function with the read/write functions and enable tpm2_key_protector module for grub-emu, so that we can further test TPM2 key unsealing with grub-emu. Signed-off-by: Gary Lin Reviewed-by: Stefan Berger Reviewed-by: Daniel Kiper Tested-by: Stefan Berger --- grub-core/Makefile.core.def | 3 +++ grub-core/kern/emu/main.c | 11 +++++++- grub-core/kern/emu/misc.c | 51 +++++++++++++++++++++++++++++++++++ grub-core/lib/tss2/tcg2_emu.c | 49 +++++++++++++++++++++++++++++++++ include/grub/emu/misc.h | 5 ++++ 5 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 grub-core/lib/tss2/tcg2_emu.c diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 97ae4e49b..40427165e 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -2574,7 +2574,9 @@ module = { common = lib/tss2/tpm2_cmd.c; common = lib/tss2/tss2.c; efi = lib/efi/tcg2.c; + emu = lib/tss2/tcg2_emu.c; enable = efi; + enable = emu; cppflags = '-I$(srcdir)/lib/tss2'; }; @@ -2586,6 +2588,7 @@ module = { common = commands/tpm2_key_protector/tpm2key_asn1_tab.c; /* The plaform support of tpm2_key_protector depends on the tcg2 implementation in tss2. */ enable = efi; + enable = emu; cppflags = '-I$(srcdir)/lib/tss2 -I$(srcdir)/lib/libtasn1-grub'; }; diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c index 855b11c3d..8a70bd7d6 100644 --- a/grub-core/kern/emu/main.c +++ b/grub-core/kern/emu/main.c @@ -55,7 +55,7 @@ static jmp_buf main_env; /* Store the prefix specified by an argument. */ -static char *root_dev = NULL, *dir = NULL; +static char *root_dev = NULL, *dir = NULL, *tpm_dev = NULL; grub_addr_t grub_modbase = 0; @@ -108,6 +108,7 @@ static struct argp_option options[] = { {"verbose", 'v', 0, 0, N_("print verbose messages."), 0}, {"hold", 'H', N_("SECS"), OPTION_ARG_OPTIONAL, N_("wait until a debugger will attach"), 0}, {"kexec", 'X', 0, 0, N_("use kexec to boot Linux kernels via systemctl (pass twice to enable dangerous fallback to non-systemctl)."), 0}, + {"tpm-device", 't', N_("DEV"), 0, N_("set TPM device."), 0}, { 0, 0, 0, 0, 0, 0 } }; @@ -168,6 +169,10 @@ argp_parser (int key, char *arg, struct argp_state *state) case 'X': grub_util_set_kexecute (); break; + case 't': + free (tpm_dev); + tpm_dev = xstrdup (arg); + break; case ARGP_KEY_ARG: { @@ -276,6 +281,9 @@ main (int argc, char *argv[]) dir = xstrdup (dir); + if (tpm_dev) + grub_util_tpm_open (tpm_dev); + /* Start GRUB! */ if (setjmp (main_env) == 0) grub_main (); @@ -283,6 +291,7 @@ main (int argc, char *argv[]) grub_fini_all (); grub_hostfs_fini (); grub_host_fini (); + grub_util_tpm_close (); grub_machine_fini (GRUB_LOADER_FLAG_NORETURN); diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c index 521220b49..1db24fde7 100644 --- a/grub-core/kern/emu/misc.c +++ b/grub-core/kern/emu/misc.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include @@ -41,6 +43,8 @@ int verbosity; int kexecute; +static int grub_util_tpm_fd = -1; + void grub_util_warn (const char *fmt, ...) { @@ -230,3 +234,50 @@ grub_util_get_kexecute (void) { return kexecute; } + +grub_err_t +grub_util_tpm_open (const char *tpm_dev) +{ + if (grub_util_tpm_fd != -1) + return GRUB_ERR_NONE; + + grub_util_tpm_fd = open (tpm_dev, O_RDWR); + if (grub_util_tpm_fd == -1) + grub_util_error (_("cannot open TPM device '%s': %s"), tpm_dev, strerror (errno)); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_util_tpm_close (void) +{ + int err; + + if (grub_util_tpm_fd == -1) + return GRUB_ERR_NONE; + + err = close (grub_util_tpm_fd); + if (err != GRUB_ERR_NONE) + grub_util_error (_("cannot close TPM device: %s"), strerror (errno)); + + grub_util_tpm_fd = -1; + return GRUB_ERR_NONE; +} + +grub_size_t +grub_util_tpm_read (void *output, grub_size_t size) +{ + if (grub_util_tpm_fd == -1) + return -1; + + return read (grub_util_tpm_fd, output, size); +} + +grub_size_t +grub_util_tpm_write (const void *input, grub_size_t size) +{ + if (grub_util_tpm_fd == -1) + return -1; + + return write (grub_util_tpm_fd, input, size); +} diff --git a/grub-core/lib/tss2/tcg2_emu.c b/grub-core/lib/tss2/tcg2_emu.c new file mode 100644 index 000000000..cab930d2b --- /dev/null +++ b/grub-core/lib/tss2/tcg2_emu.c @@ -0,0 +1,49 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2024 SUSE LLC + * Copyright (C) 2024 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 + +grub_err_t +grub_tcg2_get_max_output_size (grub_size_t *size) +{ + if (size == NULL) + return GRUB_ERR_BAD_ARGUMENT; + + *size = GRUB_TPM2_BUFFER_CAPACITY; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_tcg2_submit_command (grub_size_t input_size, grub_uint8_t *input, + grub_size_t output_size, grub_uint8_t *output) +{ + if (grub_util_tpm_write (input, input_size) != input_size) + return GRUB_ERR_BAD_DEVICE; + + if (grub_util_tpm_read (output, output_size) < sizeof (TPM_RESPONSE_HEADER_t)) + return GRUB_ERR_BAD_DEVICE; + + return GRUB_ERR_NONE; +} diff --git a/include/grub/emu/misc.h b/include/grub/emu/misc.h index fa959ebe7..fefbec499 100644 --- a/include/grub/emu/misc.h +++ b/include/grub/emu/misc.h @@ -75,4 +75,9 @@ grub_util_fopen (const char *path, const char *mode); int grub_util_file_sync (FILE *f); +grub_err_t grub_util_tpm_open (const char *tpm_dev); +grub_err_t grub_util_tpm_close (void); +grub_size_t EXPORT_FUNC(grub_util_tpm_read) (void *output, grub_size_t size); +grub_size_t EXPORT_FUNC(grub_util_tpm_write) (const void *input, grub_size_t size); + #endif /* GRUB_EMU_MISC_H */ From f898440cc1eec6e280ca149b3bea1334f28662e6 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Fri, 15 Nov 2024 15:35:00 +0800 Subject: [PATCH 241/402] tests: Add tpm2_key_protector_test For the tpm2_key_protector module, the TCG2 command submission function is the only difference between a QEMU instance and grub-emu. To test TPM2 key unsealing with a QEMU instance, it requires an extra OS image to invoke grub-protect to seal the LUKS key, rather than a simple grub-shell rescue CD image. On the other hand, grub-emu can share the emulated TPM2 device with the host, so that we can seal the LUKS key on host and test key unsealing with grub-emu. This test script firstly creates a simple LUKS image to be loaded as a loopback device in grub-emu. Then an emulated TPM2 device is created by "swtpm chardev" and PCR 0 and 1 are extended. There are several test cases in the script to test various settings. Each test case uses grub-protect or tpm2-tools to seal the LUKS password with PCR 0 and PCR 1. Then grub-emu is launched to load the LUKS image, try to mount the image with tpm2_key_protector_init and cryptomount, and verify the result. Based on the idea from Michael Chang. Cc: Michael Chang Cc: Stefan Berger Cc: Glenn Washburn Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Reviewed-by: Stefan Berger Tested-by: Stefan Berger --- Makefile.util.def | 6 + tests/tpm2_key_protector_test.in | 389 +++++++++++++++++++++++++++++++ tests/util/grub-shell.in | 6 +- 3 files changed, 400 insertions(+), 1 deletion(-) create mode 100644 tests/tpm2_key_protector_test.in diff --git a/Makefile.util.def b/Makefile.util.def index 074c0aff7..038253b37 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -1290,6 +1290,12 @@ script = { common = tests/asn1_test.in; }; +script = { + testcase = native; + name = tpm2_key_protector_test; + common = tests/tpm2_key_protector_test.in; +}; + program = { testcase = native; name = example_unit_test; diff --git a/tests/tpm2_key_protector_test.in b/tests/tpm2_key_protector_test.in new file mode 100644 index 000000000..a92e5f498 --- /dev/null +++ b/tests/tpm2_key_protector_test.in @@ -0,0 +1,389 @@ +#! @BUILD_SHEBANG@ -e + +# Test GRUBs ability to unseal a LUKS key with TPM 2.0 +# Copyright (C) 2024 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 . + +grubshell=@builddir@/grub-shell + +. "@builddir@/grub-core/modinfo.sh" + +if [ x${grub_modinfo_platform} != xemu ]; then + exit 77 +fi + +builddir="@builddir@" + +# Force build directory components +PATH="${builddir}:${PATH}" +export PATH + +if [ "x${EUID}" = "x" ] ; then + EUID=`id -u` +fi + +if [ "${EUID}" != 0 ] ; then + echo "not root; cannot test tpm2." + exit 99 +fi + +if ! command -v cryptsetup >/dev/null 2>&1; then + echo "cryptsetup not installed; cannot test tpm2." + exit 99 +fi + +if ! grep -q tpm_vtpm_proxy /proc/modules && ! modprobe tpm_vtpm_proxy; then + echo "no tpm_vtpm_proxy support; cannot test tpm2." + exit 99 +fi + +if ! command -v swtpm >/dev/null 2>&1; then + echo "swtpm not installed; cannot test tpm2." + exit 99 +fi + +if ! command -v tpm2_startup >/dev/null 2>&1; then + echo "tpm2-tools not installed; cannot test tpm2." + exit 99 +fi + +tpm2testdir="`mktemp -d "${TMPDIR:-/tmp}/$(basename "$0").XXXXXXXXXX"`" || exit 99 + +disksize=20M + +luksfile=${tpm2testdir}/luks.disk +lukskeyfile=${tpm2testdir}/password.txt + +# Choose a low iteration number to reduce the time to decrypt the disk +csopt="--type luks2 --pbkdf pbkdf2 --iter-time 1000" + +tpm2statedir=${tpm2testdir}/tpm +tpm2ctrl=${tpm2statedir}/ctrl +tpm2log=${tpm2statedir}/logfile + +sealedkey=${tpm2testdir}/sealed.tpm + +timeout=20 + +testoutput=${tpm2testdir}/testoutput + +vtext="TEST VERIFIED" + +ret=0 + +# Create the password file +echo -n "top secret" > "${lukskeyfile}" + +# Setup LUKS2 image +truncate -s ${disksize} "${luksfile}" || exit 99 +cryptsetup luksFormat -q ${csopt} "${luksfile}" "${lukskeyfile}" || exit 99 + +# Write vtext into the first block of the LUKS2 image +luksdev=/dev/mapper/`basename "${tpm2testdir}"` +cryptsetup open --key-file "${lukskeyfile}" "${luksfile}" `basename "${luksdev}"` || exit 99 +echo "${vtext}" > "${luksdev}" +cryptsetup close "${luksdev}" + +# Shutdown the swtpm instance on exit +cleanup() { + RET=$? + if [ -e "${tpm2ctrl}" ]; then + swtpm_ioctl -s --unix "${tpm2ctrl}" + fi + if [ "${RET}" -eq 0 ]; then + rm -rf "$tpm2testdir" || : + fi +} +trap cleanup EXIT INT TERM KILL QUIT + +mkdir -p "${tpm2statedir}" + +# Create the swtpm chardev instance +swtpm chardev --vtpm-proxy --tpmstate dir="${tpm2statedir}" \ + --tpm2 --ctrl type=unixio,path="${tpm2ctrl}" \ + --flags startup-clear --daemon > "${tpm2log}" || ret=$? +if [ "${ret}" -ne 0 ]; then + echo "Failed to start swtpm chardev: ${ret}" >&2 + exit 99 +fi + +# Wait for tpm2 chardev +tpm2timeout=${GRUB_TEST_SWTPM_DEFAULT_TIMEOUT:-3} +for count in `seq 1 ${tpm2timeout}`; do + sleep 1 + + tpm2dev=$(grep "New TPM device" "${tpm2log}" | cut -d' ' -f 4) + if [ -c "${tpm2dev}" ]; then + break + elif [ "${count}" -eq "${tpm2timeout}" ]; then + echo "TPM device did not appear." >&2 + exit 99 + fi +done + +# Export the TCTI variable for tpm2-tools +export TPM2TOOLS_TCTI="device:${tpm2dev}" + +# Extend PCR 0 +tpm2_pcrextend 0:sha256=$(echo "test0" | sha256sum | cut -d ' ' -f 1) || exit 99 + +# Extend PCR 1 +tpm2_pcrextend 1:sha256=$(echo "test1" | sha256sum | cut -d ' ' -f 1) || exit 99 + +tpm2_seal_unseal() { + srk_alg="$1" + handle_type="$2" + srk_test="$3" + + grub_srk_alg=${srk_alg} + + extra_opt="" + extra_grub_opt="" + + persistent_handle="0x81000000" + + grub_cfg=${tpm2testdir}/testcase.cfg + + if [ "${handle_type}" = "persistent" ]; then + extra_opt="--tpm2-srk=${persistent_handle}" + fi + + if [ "${srk_alg}" != "default" ]; then + extra_opt="${extra_opt} --tpm2-asymmetric=${srk_alg}" + fi + + # Seal the password with grub-protect + grub-protect ${extra_opt} \ + --tpm2-device="${tpm2dev}" \ + --action=add \ + --protector=tpm2 \ + --tpm2key \ + --tpm2-bank=sha256 \ + --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 + + # Flip the asymmetric algorithm in grub.cfg to trigger fallback SRKs + if [ "${srk_test}" = "fallback_srk" ]; then + if [ -z "${srk_alg##RSA*}" ]; then + grub_srk_alg="ECC" + elif [ -z "${srk_alg##ECC*}" ]; then + grub_srk_alg="RSA" + fi + fi + + if [ "${grub_srk_alg}" != "default" ] && [ "${handle_type}" != "persistent" ]; then + extra_grub_opt="-a ${grub_srk_alg}" + fi + + # Write the TPM unsealing script + cat > "${grub_cfg}" < "${testoutput}" || ret=$? + + # Remove the persistent handle + if [ "${handle_type}" = "persistent" ]; then + grub-protect \ + --tpm2-device="${tpm2dev}" \ + --protector=tpm2 \ + --action=remove \ + --tpm2-srk=${persistent_handle} \ + --tpm2-evict || : + fi + + 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 +} + +tpm2_seal_nv () { + keyfile="$1" + nv_index="$2" + pcr_list="$3" + + primary_file=${tpm2testdir}/primary.ctx + session_file=${tpm2testdir}/session.dat + policy_file=${tpm2testdir}/policy.dat + keypub_file=${tpm2testdir}/key.pub + keypriv_file=${tpm2testdir}/key.priv + name_file=${tpm2testdir}/sealing.name + sealing_ctx_file=${tpm2testdir}/sealing.ctx + + # Since we don't run a resource manager on our swtpm instance, it has + # to flush the transient handles after tpm2_createprimary, tpm2_create + # and tpm2_load to avoid the potential out-of-memory (0x902) errors. + # Ref: https://github.com/tpm2-software/tpm2-tools/issues/1338#issuecomment-469689398 + + # Create the primary object + tpm2_createprimary -Q -C o -g sha256 -G ecc -c "${primary_file}" || ret=$? + if [ "${ret}" -ne 0 ]; then + echo "Failed to create the primary object: ${ret}" >&2 + return 1 + fi + tpm2_flushcontext -t || ret=$? + if [ "${ret}" -ne 0 ]; then + echo "Failed to flush the transient handles: ${ret}" >&2 + return 1 + fi + + # Create the policy object + tpm2_startauthsession -S "${session_file}" || ret=$? + if [ "${ret}" -ne 0 ]; then + echo "Failed to start auth session: ${ret}" >&2 + return 1 + fi + tpm2_policypcr -Q -S "${session_file}" -l "${pcr_list}" -L "${policy_file}" || ret=$? + if [ "${ret}" -ne 0 ]; then + echo "Failed to create the policy object: ${ret}" >&2 + return 1 + fi + tpm2_flushcontext "${session_file}" || ret=$? + if [ "${ret}" -ne 0 ]; then + echo "Failed to flush the transient handles: ${ret}" >&2 + return 1 + fi + + # Seal the key into TPM + tpm2_create -Q \ + -C "${primary_file}" \ + -u "${keypub_file}" \ + -r "${keypriv_file}" \ + -L "${policy_file}" \ + -i "${keyfile}" || ret=$? + if [ "${ret}" -ne 0 ]; then + echo "Failed to seal \"${keyfile}\": ${ret}" >&2 + return 1 + fi + tpm2_flushcontext -t || ret=$? + if [ "${ret}" -ne 0 ]; then + echo "Failed to flush the transient handles: ${ret}" >&2 + return 1 + fi + + tpm2_load -Q \ + -C "${primary_file}" \ + -u "${keypub_file}" \ + -r "${keypriv_file}" \ + -n "${name_file}" \ + -c "${sealing_ctx_file}" || ret=$? + if [ "${ret}" -ne 0 ]; then + echo "Failed to load the sealed key into TPM: ${ret}" >&2 + return 1 + fi + tpm2_flushcontext -t || ret=$? + if [ "${ret}" -ne 0 ]; then + echo "Failed to flush the transient handles: ${ret}" >&2 + return 1 + fi + + tpm2_evictcontrol -Q -C o -c "${sealing_ctx_file}" ${nv_index} || ret=$? + if [ "${ret}" -ne 0 ]; then + echo "Failed to store the sealed key into ${nv_index}: ${ret}" >&2 + return 1 + fi + + return 0 +} + +tpm2_seal_unseal_nv() { + nv_index="0x81000000" + pcr_list="sha256:0,1" + + grub_cfg=${tpm2testdir}/testcase.cfg + + # Seal the key into a NV index guarded by PCR 0 and 1 + tpm2_seal_nv "${lukskeyfile}" ${nv_index} ${pcr_list} || ret=$? + if [ "${ret}" -ne 0 ]; then + echo "Failed to seal the secret key into ${nv_index}" >&2 + return 99 + fi + + # Write the TPM unsealing script + cat > ${grub_cfg} < "${testoutput}" || ret=$? + + # Remove the object from the NV index + tpm2_evictcontrol -Q -C o -c "${nv_index}" || : + + 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 +} + +# Testcases for SRK mode +declare -a srktests=() +srktests+=("default transient no_fallback_srk") +srktests+=("RSA transient no_fallback_srk") +srktests+=("ECC transient no_fallback_srk") +srktests+=("RSA persistent no_fallback_srk") +srktests+=("ECC persistent no_fallback_srk") +srktests+=("RSA transient fallback_srk") +srktests+=("ECC transient fallback_srk") + +for i in "${!srktests[@]}"; do + tpm2_seal_unseal ${srktests[$i]} || ret=$? + if [ "${ret}" -eq 0 ]; then + echo "TPM2 [${srktests[$i]}]: PASS" + elif [ "${ret}" -eq 1 ]; then + echo "TPM2 [${srktests[$i]}]: FAIL" + else + echo "Unexpected failure [${srktests[$i]}]" >&2 + exit ${ret} + fi +done + +# Testcase for NV index mode +tpm2_seal_unseal_nv || ret=$? +if [ "${ret}" -eq 0 ]; then + echo "TPM2 [NV Index]: PASS" +elif [ "${ret}" -eq 1 ]; then + echo "TPM2 [NV Index]: FAIL" +else + echo "Unexpected failure [NV index]" >&2 + exit ${ret} +fi + +exit 0 diff --git a/tests/util/grub-shell.in b/tests/util/grub-shell.in index ae5f711fe..15c5f45a5 100644 --- a/tests/util/grub-shell.in +++ b/tests/util/grub-shell.in @@ -75,6 +75,7 @@ work_directory=${WORKDIR:-`mktemp -d "${TMPDIR:-/tmp}/grub-shell.XXXXXXXXXX"`} | . "${builddir}/grub-core/modinfo.sh" qemuopts= +emuopts= serial_port=com0 serial_null= halt_cmd=halt @@ -376,6 +377,9 @@ for option in "$@"; do --qemu-opts=*) qs=`echo "$option" | sed -e 's/--qemu-opts=//'` qemuopts="$qemuopts $qs" ;; + --emu-opts=*) + qs=`echo "$option" | sed -e 's/--emu-opts=//'` + emuopts="$emuopts $qs" ;; --disk=*) dsk=`echo "$option" | sed -e 's/--disk=//'` if [ ${grub_modinfo_platform} = emu ]; then @@ -674,7 +678,7 @@ elif [ x$boot = xemu ]; then cat >"$work_directory/run.sh" <"$work_directory/run.sh" < Date: Fri, 15 Nov 2024 15:35:01 +0800 Subject: [PATCH 242/402] docs: Document TPM2 key protector Update the user manual to address TPM2 key protector including the two related commands, tpm2_key_protector_init and tpm2_key_protector_clear, and the user-space utility: grub-protect. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Reviewed-by: Stefan Berger Tested-by: Stefan Berger --- docs/grub.texi | 525 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 525 insertions(+) diff --git a/docs/grub.texi b/docs/grub.texi index 9202380b8..200e747af 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -6431,6 +6431,8 @@ you forget a command, you can run the command @command{help} * smbios:: Retrieve SMBIOS information * source:: Read a configuration file in same context * 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 * true:: Do nothing, successfully * trust:: Add public key to list of trusted keys * unset:: Unset an environment variable @@ -7989,6 +7991,58 @@ either @var{expression1} or @var{expression2} is true @end table @end deffn +@node tpm2_key_protector_init +@subsection tpm2_key_protector_init + +@deffn Command tpm2_key_protector_init [@option{-m} mode] | [@option{-p} pcrlist] | [@option{-b} pcrbank] | [ [@option{-T} tpm2key_file] | [@option{-k} keyfile] ] | [@option{-s} handle] | [@option{-a} srk_type] | [@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 +@option{-m}. The default mode is SRK. The main difference between SRK mode +and NV index mode is the storage of the sealed key. For SRK mode, the sealed +key is stored in a file while NV index mode stores the sealed key in the +non-volatile memory inside TPM with a given NV index. + +The @option{-p} and @option{-b} options are used to supply the PCR list and +bank that the key is sealed with. The PCR list is a comma-separated list, e.g., +'0,2,4,7,9', to represent the involved PCRs, and the default is '7'. The PCR +bank is chosen by selecting a hash algorithm. The current supported PCR banks +are SHA1, SHA256, SHA384, and SHA512, and the default is SHA256. + +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}. + +The key file for SRK mode can be supplied with either @option{-T} or +@option{-k}. The @option{-T} option is for the path to the key file in +TPM 2.0 Key File format. Since the parameters for the TPM commands are written +in the file, there is no need to set the PCR list(@option{-p}) and +bank(@option{-b}) when using the @option{-T} option. The @option{-k} option +is for the key file in the raw format, and the @option{-p} and @option{-b} +options are necessary for the non-default PCR list or bank. In general, +TPM 2.0 Key File format is preferred due to the simplified GRUB command +options and the authorized policy support + +Besides the key file, there are two options, @option{-a} and @option{-s}, to +tweak the TPM Storage Root Key (SRK). The SRK can be either created at +runtime or stored in the non-volatile memory. When creating SRK at runtime, +GRUB provides the SRK template to the TPM to create the key. There are two SRK +templates for the @option{-a} option, ECC and RSA, and the default is ECC. +If the SRK is stored in a specific handle, e.g. @code{0x81000001}, the +@option{-s} option can be used to set the handle to notify GRUB to load +the SRK from the given handle. + +The only NV index-specific option is the @option{-n} option which is used to +set the NV index containing the sealed key. Then GRUB can load the sealed +key and unseal it with the given PCR list and bank. +@end deffn + +@node tpm2_key_protector_clear +@subsection tpm2_key_protector_clear + +@deffn Command tpm2_key_protector_clear +Clear the TPM2 key protector if previously initialized. +@end deffn @node true @subsection true @@ -8517,6 +8571,7 @@ environment variables and commands are listed in the same order. * 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 @@ -8760,6 +8815,310 @@ be restricted and some operations/commands cannot be executed. The @samp{lockdown} variable is set to @samp{y} when the GRUB is locked down. Otherwise it does not exit. +@node TPM2 key protector +@section TPM2 key protector in GRUB + +TPM2 key protector extends measured boot to unlock the encrypted partition +without user intervention. It uses the TPM Storage Root Key (SRK) to seal +the disk key with a given set of PCR values. If the system state matches, +i.e. PCR values match the sealed PCR set, TPM2 key protector unseals the +disk key for @command{cryptomount} (@pxref{cryptomount}) to unlock the +encrypted partition. In case the unsealed key fails to unlock the +partition, @command{cryptomount} falls back to the passphrase prompt. + +Please note that TPM2 key protector uses the SRK in the owner hierarchy +@emph{without} authorization. If the owner hierarchy is password-protected, +TPM2 key protector may fail to unseal the key due to the absence of the +password. For the systems that already enable the password protection for the +owner hierarchy, the following command removes the password protection with +the existing password. + +@example +# @kbd{tpm2_changeauth -c owner -p password} +@end example + +There are two supported modes to store the sealed key, SRK and NV index. +The details will be addressed in later sections. + +TPM2 key protector is currently only supported on EFI and EMU platforms. + +@subsection TPM PCR usage + +Since TPM2 key protector relies on PCRs to check the system state, it is +important to decide which PCRs to seal the key with. The following table +lists uses of PCRs and the measured objects on EFI platforms. + +@multitable @columnfractions 0.1 0.2 0.7 +@headitem PCR @tab Used by @tab Measured Objects +@item 0 +@tab Firmware +@tab Core system firmware executable code +@item 1 +@tab Firmware +@tab Core system firmware data/host platform configuration; typically +contains serial and model numbers +@item 2 +@tab Firmware +@tab Extended or pluggable executable code; includes option ROMs on +pluggable hardware +@item 3 +@tab Firmware +@tab Extended or pluggable firmware data; includes information about +pluggable hardware +@item 4 +@tab Firmware +@tab Boot loader and additional drivers; binaries and extensions loaded +by the boot loader +@item 5 +@tab Firmware +@tab GPT/Partition table +@item 7 +@tab Firmware +@tab SecureBoot state +@item 8 +@tab GRUB +@tab Commands and kernel command line +@item 9 +@tab GRUB +@tab All files read (including kernel image) +@item 9 +@tab Linux Kernel +@tab All passed initrds (when the new LOAD_FILE2 initrd protocol is used) +@item 10 +@tab Linux Kernel +@tab Protection of the IMA measurement log +@item 14 +@tab shim +@tab “MOK” certificates and hashes +@end multitable + +PCR 0, 2, 4, and 7 can be used to check the integrity of the firmware code +and bootloaders. PCR 8 and 9 are useful to check the file and data processed +by GRUB. PCRs 10, 11, 12, 13, and 15 are controlled by the operating system, +so those PCRs are usually still in the initial state when GRUB is running. + +In general, it is nice to include PCR 0, 2, 4, and 7 to ensure the integrity +of the firmware and bootloaders. For PCR 8 and 9, a sophisticated tool is +required to examine the GRUB configuration files and the files to be loaded +to calculate the correct PCR values. + +Please note that PCRs are sensitive to any change, so an update of a component +could invalidate the sealed key, due to the so-called PCR brittleness. For the +bootloader update, PCR 4 may be affected. This can be mitigated by extracting +the events from the TPM event log and predict the value with the updated +bootloader binary. On the other hand, it is difficult to predict PCR 0~7 after +a firmware update since the content of the code and the order of drivers may +not follow the TPM event log from the previous firmware version, so it is +necessary to reboot the system to update the measurement results of PCR 0~7 +and seal or sign the sealed key again. + +Reference: @url{https://uapi-group.org/specifications/specs/linux_tpm_pcr_registry/, Linux TPM PCR Registry} + +@subsection Setting up the extra disk key + +Instead of using the existing password, it is recommended to seal a new +random disk key and use the existing password for recovery. + +Here are the sample commands to create a 128 random bytes key file and +enroll the key into the target partition (sda2). + +@example +# @kbd{dd if=/dev/urandom of=luks.key bs=1 count=128} +# @kbd{cryptsetup luksAddKey /dev/sda2 luks.key --pbkdf=pbkdf2 --hash=sha512} +@end example + +@subsection SRK mode + +To unlock the partition with SRK mode, assume that the sealed key is in +@file{(hd0,gpt1)/efi/grub/sealed.tpm}, the following GRUB commands +unseal the disk key with SRK mode and supply it to @command{cryptomount}. + +@example +grub> @kbd{tpm2_key_protector_init -T (hd0,gpt1)/efi/grub/sealed.tpm} +grub> @kbd{cryptomount -u -P tpm2} +@end example + +There are two programs to create the sealed key for SRK mode: @command{grub-protect} +and @command{pcr-oracle} (@url{https://github.com/okirch/pcr-oracle}). + +The following sample command uses @command{grub-protect} to seal the random +key, @file{luks.key}, with PCR 0, 2, 4 and 7 in TPM 2.0 Key File format. + +@example +@group +# @kbd{grub-protect --action=add \ + --protector=tpm2 \ + --tpm2-pcrs=0,2,4,7 \ + --tpm2key \ + --tpm2-keyfile=luks.key \ + --tpm2-outfile=/boot/efi/efi/grub/sealed.tpm} +@end group +@end example + +@command{grub-protect} only seals the key with the current PCR values. +Therefore, when a boot component, such as shim or GRUB, is updated, it is +necessary to reboot the system to update the measurement results and seal +the key again. That means the random disk key has to be stored in cleartext +for the next key sealing. Besides this, the measurement result of some PCRs +may differ between boot time and OS runtime. For example, PCR 9 measures the +files loaded by GRUB including the Linux kernel and initrd. To unlock the disk +containing the kernel and initrd, the key has to be sealed with PCR 9 value +before loading the kernel and initrd. However, PCR 9 changes after GRUB +loading the kernel and initrd, so PCR 9 at OS runtime cannot be used directly +for key sealing. + +To solve these problems, @command{pcr-oracle} takes a different approach. It +reads the TPM eventlog and predicts the PCR values. Besides, +@command{pcr-oracle} also supports ``authorized policy'' which allows the +PCR policy to be updated with a valid signature, so that the user only seals +the random disk key once. If at some later time the PCR values change due to +an update of the system firmware, bootloader, or config file, the user just +needs to update the signature of the PCR policy. + +To seal the key with the authorized policy, the first thing is to generate +the RSA policy key, @file{policy-key.pem}, and the authorized policy file, +@file{authorized.policy}. In this example, PCR 0, 2, 4, 7 and 9 are chosen +for key sealing. + +@example +@group +# @kbd{pcr-oracle --rsa-generate-key \ + --private-key policy-key.pem \ + --auth authorized.policy \ + create-authorized-policy 0,2,4,7,9} +@end group +@end example + +Then, we seal the random disk key, @file{luks.key}, with the authorized +policy file and save the sealed key in @file{sealed.key}. + +@example +@group +# @kbd{pcr-oracle --key-format tpm2.0 \ + --auth authorized.policy \ + --input luks.key \ + --output sealed.key \ + seal-secret} +@end group +@end example + +Since we now have the sealed key, we can remove the random disk key file +@file{luks.key}. + +The last step is to sign the predicted PCR policy and save the final key +file, @file{sealed.tpm}. + +@example +@group +# @kbd{pcr-oracle --key-format tpm2.0 \ + --private-key policy-key.pem \ + --from eventlog \ + --stop-event "grub-file=grub.cfg" \ + --after \ + --input sealed.key \ + --output /boot/efi/efi/grub/sealed.tpm \ + sign 0,2,4,7,9} +@end group +@end example + +Here we also set a stop event for the prediction. With +@kbd{--stop-event grub-file=grub.cfg --after}, @command{pcr-oracle} stops +the calculation of PCR values right after GRUB loads @file{grub.cfg}. + +When/After the shim or GRUB are updated, it only requires to run the last +@command{pcr-oracle} command to update the predicted PCR policy. + +@subsection NV index mode + +Instead of storing the sealed key in a file, NV index mode uses the TPM +non-volatile memory to store the sealed key. + +The following sample commands use tpm2-tools (@url{https://github.com/tpm2-software/tpm2-tools}) +commands to seal @file{luks.key} into the specific NV index: @kbd{0x81000000}. + +First, we need to create the object file for the primary key, i.e. storage +root key (SRK) with the default key settings in GRUB: SHA256 hash algorithm +and ECC key algorithm. + +@example +# @kbd{tpm2_createprimary -C o -g sha256 -G ecc -c primary.ctx} +@end example + +The next commands collect the current values of PCR 0, 2, 4, and 7 and saves +them in @file{pcr.dat}. + +@example +# @kbd{tpm2_startauthsession -S session.dat} +# @kbd{tpm2_policypcr -S session.dat -l sha256:0,2,4,7 -f pcrs.dat -L policy.dat} +# @kbd{tpm2_flushcontext session.dat} +@end example + +The last commands seal @file{luks.key} with the primary key and stores the +result in @kbd{0x81000000}. + +@example +# @kbd{cat luks.key | tpm2_create -C primary.ctx -u key.pub -r key.priv -L policy.dat -i-} +# @kbd{tpm2_load -C primary.ctx -u key.pub -r key.priv -n sealing.name -c sealing.ctx} +# @kbd{tpm2_evictcontrol -C o -c sealing.ctx 0x81000000} +@end example + +To unseal the key, we have to specify the mode @kbd{nv}, the NV index +@kbd{0x81000000}, and the PCRs @kbd{0,2,4,7} for the @command{tpm2_key_protector_init} +command. + +@example +grub> @kbd{tpm2_key_protector_init --mode=nv --nvindex=0x81000000 --pcrs=0,2,4,7} +grub> @kbd{cryptomount -u --protector tpm2} +@end example + +@subsection Setting up software TPM for EMU platform + +In order to test TPM2 key protector and TPM2 Software Stack (TSS2), it is +useful to set up a software TPM (swtpm) instance and run the commands on the +EMU platform. + +Here are the commands to start a swtpm instance which provides a character +device interface. To store the TPM states, the directory, @file{swtpm-state}, +is created before the @command{swtpm} command. All the messages are stored +in @file{swtpm.log} including the name of the character device. + +@example +# @kbd{mkdir swtpm-state} +@group +# @kbd{swtpm chardev --vtpm-proxy --tpmstate dir=swtpm-state \ + --tpm2 --ctrl type=unixio,path="swtpm-state/ctrl" \ + --flags startup-clear --daemon > swtpm.log} +@end group +@end example + +Then, we extract the name of the character device from @file{swtpm.log} and +save it to the variable, @samp{tpm2dev}. + +@example +# @kbd{tpm2dev=$(grep "New TPM device" swtpm.log | cut -d' ' -f 4)} +@end example + +Now we can start @kbd{grub-emu} with @kbd{--tpm-device $tpm2dev} to interact +with the swtpm instance. + +@example +# @kbd{grub-emu --tpm-device $tpm2dev} +@end example + +On the host, the tpm2-tools commands can interact with the swtpm instance by +setting @samp{TPM2TOOLS_TCTI}. + +@example +# @kbd{export TPM2TOOLS_TCTI="device:$tpm2dev"} +@end example + +When the test is done, use @kbd{swtpm_ioctl} to send the shutdown +command through the swtpm control channel. + +@example +# @kbd{swtpm_ioctl -s --unix swtpm-state/ctrl} +@end example + @node Platform limitations @chapter Platform limitations @@ -9200,6 +9559,7 @@ bootability on other machines. * Invoking grub-mkrescue:: Make a GRUB rescue image * Invoking grub-mount:: Mount a file system using GRUB * Invoking grub-probe:: Probe device information for GRUB +* Invoking grub-protect:: Protect a disk key with a key protector * Invoking grub-script-check:: Check GRUB script file for syntax errors @end menu @@ -9582,6 +9942,171 @@ Print verbose messages. @end table +@node Invoking grub-protect +@section Invoking grub-protect + +The program @command{grub-protect} protects a disk encryption key with +a specified key protector. + +@table @option +@item --help +Print a summary of the command-line options and exit. + +@item --version +Print the version number of GRUB and exit. + +@item -a add|remove +@itemx --action=add|remove +Add or remove a key protector to or from a key. + +@item -p @var{protector} +@itemx --protector=@var{protector} +Set the key protector. Currently, @samp{tpm2} is the only supported key +protector. + +@item --tpm2-asymmetric=@var{type} +Choose the the type of SRK. The valid options are @samp{RSA} (@samp{RSA2048}) +and @samp{ECC} (@samp{ECC_NIST_P256}).(default: @samp{ECC}) + +@item --tpm2-bank=@var{alg} +Choose bank of PCRs used to authorize key release: @samp{SHA1}, @samp{SHA256}, +@samp{SHA384}, or @samp{SHA512}. (default: @samp{SHA256}) + +@item --tpm2-device=@var{device} +Set the path to the TPM2 device. (default: @samp{/dev/tpm0}) + +@item --tpm2-evict +Evict a previously persisted SRK from the TPM, if any. + +@item --tpm2-keyfile=@var{file} +Set the path to a file that contains the cleartext key to protect. + +@item --tpm2-outfile=@var{file} +Set the path to the file that will contain the key after sealing +(must be accessible to GRUB during boot). + +@item --tpm2-pcrs=@var{pcrs} +Set a comma-separated list of PCRs used to authorize key release e.g., @samp{7,11}. +Please be aware that PCR 0~7 are used by the firmware and the measurement result +may change after a firmware update (for baremetal systems) or a package +(OVMF/SLOF) update in the VM host. This may lead to the failure of key +unsealing. (default: @samp{7}) + +@item --tpm2-srk=@var{handle} +Set the SRK handle, e.g. @samp{0x81000000}, if the SRK is to be made persistent. + +@item --tpm2key +Use TPM 2.0 Key File format. + +@end table + +Before sealing the key, please check the TPM PCR usage +(@pxref{TPM2 key protector, TPM PCR usage}) to choose a proper set of PCRs. + +Assume that there is a key file, @file{luks.key}, to be sealed with PCR 0, 2, +4, and 7, and here is the @command{grub-protect} command to create the sealed +key file: + +@example +@group +# @kbd{grub-protect --action=add \ + --protector=tpm2 \ + --tpm2-pcrs=0,2,4,7 \ + --tpm2key \ + --tpm2-keyfile=luks.key \ + --tpm2-outfile=/boot/efi/efi/grub/sealed.tpm} +@end group +@end example + +Then, GRUB can unlock the target partition with the following commands: + +@example +grub> @kbd{tpm2_key_protector_init -T (hd0,gpt1)/efi/grub/sealed.tpm} +grub> @kbd{cryptomount -u -P tpm2} +@end example + +In most of cases, the user only needs to create the key with the `add' action. +If auto-unlocking is unwanted, just remove the file and the +@command{tpm2_key_protector_init} command and invoke the @command{cryptomount} +command without @kbd{-P tpm2}. + +The only use case for the `remove' action is when the SRK is made persistent. + +There are two supported SRKs in @command{grub-protect}: @samp{RSA} and @samp{ECC}. +Due to slower key generation, some users of the @samp{RSA} SRK may prefer +making it persistent so that the TPM can skip the SRK generation when GRUB tries +to unseal the key. + +The available persistent handles can be checked with @command{tpm2_getcap}. + +@example +@group +# @kbd{tpm2_getcap properties-variable} +... +TPM2_PT_HR_PERSISTENT: 0x0 +TPM2_PT_HR_PERSISTENT_AVAIL: 0x41 +... +@end group +@end example + +In this system, there is no persistent handle. A TPM handle is an unsigned +32-bit integer, and the persistent handles starts with @samp{0x81}. Here +we choose the well-known persistent handle: @samp{0x81000000}. + +@example +@group +# @kbd{grub-protect --action=add \ + --protector=tpm2 \ + --tpm2-pcrs=0,2,4,7 \ + --tpm2-asymmetric=RSA \ + --tpm2-srk=0x81000000 \ + --tpm2key \ + --tpm2-keyfile=luks.key \ + --tpm2-outfile=/boot/efi/efi/grub/sealed.tpm} +@end group +@end example + +The additional @kbd{--tpm2-asymmetric=RSA} and @kbd{--tpm2-srk=0x81000000} +options are used to make the key sealed with the RSA SRK and store the SRK +in @samp{0x81000000}. + +For the @command{tpm2_key_protector_init} command, the additional @kbd{-s 0x81000000} +informs the TPM2 key protector to fetch the SRK from @samp{0x81000000}. + +@example +grub> @kbd{tpm2_key_protector_init -s 0x81000000 -T (hd0,gpt1)/efi/grub/sealed.tpm} +grub> @kbd{cryptomount -u -P tpm2} +@end example + +After making the SRK handle persistent, we can check the status of the +persistent handles with @command{tpm2_getcap}. + +@example +@group +# @kbd{tpm2_getcap properties-variable} +... +TPM2_PT_HR_PERSISTENT: 0x1 +TPM2_PT_HR_PERSISTENT_AVAIL: 0x40 +... +# @kbd{tpm2_getcap handles-persistent} +- 0x81000000 +@end group +@end example + +The sealed key can be removed once the user does not want to use the TPM2 key +protector anymore. Here is the command to remove the persistent SRK handle +(@samp{0x81000000}) with @kbd{--tpm2-srk} and @kbd{--tpm2-evict}. + +@example +@group +# @kbd{grub-protect --action=remove \ + --protector=tpm2 \ + --tpm2-srk 0x81000000 \ + --tpm2-evict} +@end group +@end example + + @node Invoking grub-script-check @section Invoking grub-script-check From 99ee68a0149b1132f160c80924ab2987ebafcbdd Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Tue, 26 Nov 2024 15:39:40 -0500 Subject: [PATCH 243/402] tss2: Adjust bit fields for big endian targets The TPM bit fields need to be in reverse order for big endian targets, such as ieee1275 PowerPC platforms that run GRUB in big endian mode. Signed-off-by: Stefan Berger Reviewed-by: Gary Lin Reviewed-by: Daniel Kiper --- grub-core/lib/tss2/tss2_structs.h | 38 +++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/grub-core/lib/tss2/tss2_structs.h b/grub-core/lib/tss2/tss2_structs.h index ca33db3ec..e5390ab56 100644 --- a/grub-core/lib/tss2/tss2_structs.h +++ b/grub-core/lib/tss2/tss2_structs.h @@ -147,6 +147,15 @@ typedef TPM2B_DIGEST_t TPM2B_NONCE_t; /* TPMA_SESSION Structure */ struct TPMA_SESSION { +#ifdef GRUB_TARGET_WORDS_BIGENDIAN + grub_uint8_t audit:1; + grub_uint8_t encrypt:1; + grub_uint8_t decrypt:1; + grub_uint8_t reserved:2; + grub_uint8_t auditReset:1; + grub_uint8_t auditExclusive:1; + grub_uint8_t continueSession:1; +#else grub_uint8_t continueSession:1; grub_uint8_t auditExclusive:1; grub_uint8_t auditReset:1; @@ -154,6 +163,7 @@ struct TPMA_SESSION grub_uint8_t decrypt:1; grub_uint8_t encrypt:1; grub_uint8_t audit:1; +#endif }; typedef struct TPMA_SESSION TPMA_SESSION_t; @@ -206,6 +216,24 @@ typedef struct TPM2B_SENSITIVE_CREATE TPM2B_SENSITIVE_CREATE_t; /* TPMA_OBJECT Structure */ struct TPMA_OBJECT { +#ifdef GRUB_TARGET_WORDS_BIGENDIAN + grub_uint32_t reserved5:13; + grub_uint32_t sign:1; + grub_uint32_t decrypt:1; + grub_uint32_t restricted:1; + grub_uint32_t reserved4:4; + grub_uint32_t encryptedDuplication:1; + grub_uint32_t noDA:1; + grub_uint32_t reserved3:2; + grub_uint32_t adminWithPolicy:1; + grub_uint32_t userWithAuth:1; + grub_uint32_t sensitiveDataOrigin:1; + grub_uint32_t fixedParent:1; + grub_uint32_t reserved2:1; + grub_uint32_t stClear:1; + grub_uint32_t fixedTPM:1; + grub_uint32_t reserved1:1; +#else grub_uint32_t reserved1:1; grub_uint32_t fixedTPM:1; grub_uint32_t stClear:1; @@ -222,6 +250,7 @@ struct TPMA_OBJECT grub_uint32_t decrypt:1; grub_uint32_t sign:1; grub_uint32_t reserved5:13; +#endif }; typedef struct TPMA_OBJECT TPMA_OBJECT_t; @@ -516,12 +545,21 @@ typedef struct TPM2B_DATA TPM2B_DATA_t; /* TPMA_LOCALITY Structure */ struct TPMA_LOCALITY { +#ifdef GRUB_TARGET_WORDS_BIGENDIAN + grub_uint8_t Extended:3; + grub_uint8_t TPM_LOC_FOUR:1; + grub_uint8_t TPM_LOC_THREE:1; + grub_uint8_t TPM_LOC_TWO:1; + grub_uint8_t TPM_LOC_ONE:1; + grub_uint8_t TPM_LOC_ZERO:1; +#else grub_uint8_t TPM_LOC_ZERO:1; grub_uint8_t TPM_LOC_ONE:1; grub_uint8_t TPM_LOC_TWO:1; grub_uint8_t TPM_LOC_THREE:1; grub_uint8_t TPM_LOC_FOUR:1; grub_uint8_t Extended:3; +#endif }; typedef struct TPMA_LOCALITY TPMA_LOCALITY_t; From 29d1bd2a96948bc120cb5906188117f670797fcf Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Tue, 26 Nov 2024 15:39:41 -0500 Subject: [PATCH 244/402] term/ieee1275/serial: Cast 0 to proper type Cast 0 to proper type grub_ieee1275_ihandle_t. This type is used for struct grub_serial_port's handle that assigns or compares with IEEE1275_IHANDLE_INVALID. Signed-off-by: Stefan Berger Reviewed-by: Daniel Kiper --- grub-core/term/ieee1275/serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/term/ieee1275/serial.c b/grub-core/term/ieee1275/serial.c index 0e4cac4c4..9bc44b306 100644 --- a/grub-core/term/ieee1275/serial.c +++ b/grub-core/term/ieee1275/serial.c @@ -25,7 +25,7 @@ #include #include -#define IEEE1275_IHANDLE_INVALID ((grub_ieee1275_cell_t) 0) +#define IEEE1275_IHANDLE_INVALID ((grub_ieee1275_ihandle_t) 0) struct ofserial_hash_ent { From 7344b3c7cee8dea94dbc97211c5e6d1925848865 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Tue, 26 Nov 2024 15:39:42 -0500 Subject: [PATCH 245/402] ieee1275: Consolidate repeated definitions of IEEE1275_IHANDLE_INVALID Consolidate repeated definitions of IEEE1275_IHANDLE_INVALID that are cast to the type grub_ieee1275_ihandle_t. On the occasion add "GRUB_" prefix to the constant name. Signed-off-by: Stefan Berger Reviewed-by: Daniel Kiper --- grub-core/commands/ieee1275/ibmvtpm.c | 4 +--- grub-core/term/ieee1275/serial.c | 8 +++----- include/grub/ieee1275/ieee1275.h | 1 + 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/grub-core/commands/ieee1275/ibmvtpm.c b/grub-core/commands/ieee1275/ibmvtpm.c index a6fee5c51..dd30c7432 100644 --- a/grub-core/commands/ieee1275/ibmvtpm.c +++ b/grub-core/commands/ieee1275/ibmvtpm.c @@ -29,8 +29,6 @@ static grub_ieee1275_ihandle_t tpm_ihandle; static grub_uint8_t tpm_version; -#define IEEE1275_IHANDLE_INVALID ((grub_ieee1275_ihandle_t) 0) - static void tpm_get_tpm_version (void) { @@ -53,7 +51,7 @@ tpm_init (void) { if (grub_ieee1275_open ("/vdevice/vtpm", &tpm_ihandle) < 0) { - tpm_ihandle = IEEE1275_IHANDLE_INVALID; + tpm_ihandle = GRUB_IEEE1275_IHANDLE_INVALID; return GRUB_ERR_UNKNOWN_DEVICE; } diff --git a/grub-core/term/ieee1275/serial.c b/grub-core/term/ieee1275/serial.c index 9bc44b306..ac2a8f827 100644 --- a/grub-core/term/ieee1275/serial.c +++ b/grub-core/term/ieee1275/serial.c @@ -25,8 +25,6 @@ #include #include -#define IEEE1275_IHANDLE_INVALID ((grub_ieee1275_ihandle_t) 0) - struct ofserial_hash_ent { char *devpath; @@ -44,7 +42,7 @@ do_real_config (struct grub_serial_port *port) if (grub_ieee1275_open (port->elem->devpath, &port->handle) || port->handle == (grub_ieee1275_ihandle_t) -1) - port->handle = IEEE1275_IHANDLE_INVALID; + port->handle = GRUB_IEEE1275_IHANDLE_INVALID; port->configured = 1; } @@ -58,7 +56,7 @@ serial_hw_fetch (struct grub_serial_port *port) do_real_config (port); - if (port->handle == IEEE1275_IHANDLE_INVALID) + if (port->handle == GRUB_IEEE1275_IHANDLE_INVALID) return -1; grub_ieee1275_read (port->handle, &c, 1, &actual); @@ -76,7 +74,7 @@ serial_hw_put (struct grub_serial_port *port, const int c) do_real_config (port); - if (port->handle == IEEE1275_IHANDLE_INVALID) + if (port->handle == GRUB_IEEE1275_IHANDLE_INVALID) return; grub_ieee1275_write (port->handle, &c0, 1, &actual); diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h index dddb38514..c445d0499 100644 --- a/include/grub/ieee1275/ieee1275.h +++ b/include/grub/ieee1275/ieee1275.h @@ -60,6 +60,7 @@ struct grub_ieee1275_common_hdr typedef grub_uint32_t grub_ieee1275_ihandle_t; typedef grub_uint32_t grub_ieee1275_phandle_t; +#define GRUB_IEEE1275_IHANDLE_INVALID ((grub_ieee1275_ihandle_t) 0) #define GRUB_IEEE1275_PHANDLE_INVALID ((grub_ieee1275_phandle_t) -1) struct grub_ieee1275_devalias From 8c0b5f200352603e53b799fca7b63f845a978f19 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Tue, 26 Nov 2024 15:39:43 -0500 Subject: [PATCH 246/402] ieee1275/ibmvpm: Move TPM initialization functions to own file Move common initialization functions from the ibmvtpm driver module into tcg2.c that will be moved into the new TCG2 driver in a subsequent patch. Make the functions available to the ibmvtpm driver as public functions and variables. Signed-off-by: Stefan Berger Reviewed-by: Daniel Kiper --- grub-core/Makefile.core.def | 1 + grub-core/commands/ieee1275/ibmvtpm.c | 44 ++----------------- grub-core/lib/ieee1275/tcg2.c | 61 +++++++++++++++++++++++++++ include/grub/ieee1275/tpm.h | 31 ++++++++++++++ 4 files changed, 97 insertions(+), 40 deletions(-) create mode 100644 grub-core/lib/ieee1275/tcg2.c create mode 100644 include/grub/ieee1275/tpm.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 40427165e..c5fd796d4 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1155,6 +1155,7 @@ module = { name = tpm; common = commands/tpm.c; ieee1275 = commands/ieee1275/ibmvtpm.c; + ieee1275 = lib/ieee1275/tcg2.c; enable = powerpc_ieee1275; }; diff --git a/grub-core/commands/ieee1275/ibmvtpm.c b/grub-core/commands/ieee1275/ibmvtpm.c index dd30c7432..284673217 100644 --- a/grub-core/commands/ieee1275/ibmvtpm.c +++ b/grub-core/commands/ieee1275/ibmvtpm.c @@ -23,46 +23,10 @@ #include #include #include +#include #include #include -static grub_ieee1275_ihandle_t tpm_ihandle; -static grub_uint8_t tpm_version; - -static void -tpm_get_tpm_version (void) -{ - grub_ieee1275_phandle_t vtpm; - char buffer[20]; - - if (!grub_ieee1275_finddevice ("/vdevice/vtpm", &vtpm) && - !grub_ieee1275_get_property (vtpm, "compatible", buffer, - sizeof (buffer), NULL) && - !grub_strcmp (buffer, "IBM,vtpm20")) - tpm_version = 2; -} - -static grub_err_t -tpm_init (void) -{ - static int init_success = 0; - - if (!init_success) - { - if (grub_ieee1275_open ("/vdevice/vtpm", &tpm_ihandle) < 0) - { - tpm_ihandle = GRUB_IEEE1275_IHANDLE_INVALID; - return GRUB_ERR_UNKNOWN_DEVICE; - } - - init_success = 1; - - tpm_get_tpm_version (); - } - - return GRUB_ERR_NONE; -} - static int ibmvtpm_2hash_ext_log (grub_uint8_t pcrindex, grub_uint32_t eventtype, @@ -88,7 +52,7 @@ ibmvtpm_2hash_ext_log (grub_uint8_t pcrindex, INIT_IEEE1275_COMMON (&args.common, "call-method", 8, 2); args.method = (grub_ieee1275_cell_t) "2hash-ext-log"; - args.ihandle = tpm_ihandle; + args.ihandle = grub_ieee1275_tpm_ihandle; args.pcrindex = pcrindex; args.eventtype = eventtype; args.description = (grub_ieee1275_cell_t) description; @@ -136,7 +100,7 @@ grub_tpm_measure (unsigned char *buf, grub_size_t size, grub_uint8_t pcr, grub_dprintf ("tpm", "log_event, pcr = %d, size = 0x%" PRIxGRUB_SIZE ", %s\n", pcr, size, description); - if (tpm_version == 2) + if (grub_ieee1275_tpm_version == 2) return tpm2_log_event (buf, size, pcr, description); return GRUB_ERR_NONE; @@ -149,5 +113,5 @@ grub_tpm_present (void) * Call tpm_init() "late" rather than from GRUB_MOD_INIT() so that device nodes * can be found. */ - return tpm_init() == GRUB_ERR_NONE; + return grub_ieee1275_tpm_init() == GRUB_ERR_NONE; } diff --git a/grub-core/lib/ieee1275/tcg2.c b/grub-core/lib/ieee1275/tcg2.c new file mode 100644 index 000000000..1819d1447 --- /dev/null +++ b/grub-core/lib/ieee1275/tcg2.c @@ -0,0 +1,61 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2024 IBM Corporation + * Copyright (C) 2024 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 + +grub_ieee1275_ihandle_t grub_ieee1275_tpm_ihandle; +grub_uint8_t grub_ieee1275_tpm_version; + +static void +tpm_get_tpm_version (void) +{ + grub_ieee1275_phandle_t vtpm; + char buffer[20]; + + if (!grub_ieee1275_finddevice ("/vdevice/vtpm", &vtpm) && + !grub_ieee1275_get_property (vtpm, "compatible", buffer, + sizeof (buffer), NULL) && + !grub_strcmp (buffer, "IBM,vtpm20")) + grub_ieee1275_tpm_version = 2; +} + +grub_err_t +grub_ieee1275_tpm_init (void) +{ + static int init_success = 0; + + if (!init_success) + { + if (grub_ieee1275_open ("/vdevice/vtpm", &grub_ieee1275_tpm_ihandle) < 0) + { + grub_ieee1275_tpm_ihandle = GRUB_IEEE1275_IHANDLE_INVALID; + return GRUB_ERR_UNKNOWN_DEVICE; + } + + init_success = 1; + + tpm_get_tpm_version (); + } + + return GRUB_ERR_NONE; +} diff --git a/include/grub/ieee1275/tpm.h b/include/grub/ieee1275/tpm.h new file mode 100644 index 000000000..9575c1c68 --- /dev/null +++ b/include/grub/ieee1275/tpm.h @@ -0,0 +1,31 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2024 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 GRUB_IEEE1275_TPM_HEADER +#define GRUB_IEEE1275_TPM_HEADER 1 + +#include +#include +#include + +extern grub_ieee1275_ihandle_t grub_ieee1275_tpm_ihandle; +extern grub_uint8_t grub_ieee1275_tpm_version; + +extern grub_err_t grub_ieee1275_tpm_init (void); + +#endif From 72092a8641958af95402fa0e3e74cc57c36feefa Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Tue, 26 Nov 2024 15:39:44 -0500 Subject: [PATCH 247/402] ieee1275/tcg2: Refactor grub_ieee1275_tpm_init() Move tpm_get_tpm_version() into grub_ieee1275_tpm_init() and invalidate grub_ieee1275_tpm_ihandle in case no TPM 2 could be detected. Try the initialization only once so that grub_tpm_present() will always return the same result. Use the grub_ieee1275_tpm_ihandle as indicator for an available TPM instead of grub_ieee1275_tpm_version, which can now be removed. Signed-off-by: Stefan Berger Reviewed-by: Daniel Kiper --- grub-core/commands/ieee1275/ibmvtpm.c | 2 +- grub-core/lib/ieee1275/tcg2.c | 40 ++++++++++++--------------- include/grub/ieee1275/tpm.h | 1 - 3 files changed, 18 insertions(+), 25 deletions(-) diff --git a/grub-core/commands/ieee1275/ibmvtpm.c b/grub-core/commands/ieee1275/ibmvtpm.c index 284673217..4958b04a9 100644 --- a/grub-core/commands/ieee1275/ibmvtpm.c +++ b/grub-core/commands/ieee1275/ibmvtpm.c @@ -100,7 +100,7 @@ grub_tpm_measure (unsigned char *buf, grub_size_t size, grub_uint8_t pcr, grub_dprintf ("tpm", "log_event, pcr = %d, size = 0x%" PRIxGRUB_SIZE ", %s\n", pcr, size, description); - if (grub_ieee1275_tpm_version == 2) + if (grub_ieee1275_tpm_ihandle != GRUB_IEEE1275_IHANDLE_INVALID) return tpm2_log_event (buf, size, pcr, description); return GRUB_ERR_NONE; diff --git a/grub-core/lib/ieee1275/tcg2.c b/grub-core/lib/ieee1275/tcg2.c index 1819d1447..ea01a30eb 100644 --- a/grub-core/lib/ieee1275/tcg2.c +++ b/grub-core/lib/ieee1275/tcg2.c @@ -23,39 +23,33 @@ #include #include -grub_ieee1275_ihandle_t grub_ieee1275_tpm_ihandle; -grub_uint8_t grub_ieee1275_tpm_version; - -static void -tpm_get_tpm_version (void) -{ - grub_ieee1275_phandle_t vtpm; - char buffer[20]; - - if (!grub_ieee1275_finddevice ("/vdevice/vtpm", &vtpm) && - !grub_ieee1275_get_property (vtpm, "compatible", buffer, - sizeof (buffer), NULL) && - !grub_strcmp (buffer, "IBM,vtpm20")) - grub_ieee1275_tpm_version = 2; -} +grub_ieee1275_ihandle_t grub_ieee1275_tpm_ihandle = GRUB_IEEE1275_IHANDLE_INVALID; grub_err_t grub_ieee1275_tpm_init (void) { - static int init_success = 0; + static bool init_tried = false; + grub_ieee1275_phandle_t vtpm; + char buffer[20]; - if (!init_success) + if (init_tried == false) { - if (grub_ieee1275_open ("/vdevice/vtpm", &grub_ieee1275_tpm_ihandle) < 0) + init_tried = true; + + if (grub_ieee1275_open ("/vdevice/vtpm", &grub_ieee1275_tpm_ihandle) < 0 || + grub_ieee1275_finddevice ("/vdevice/vtpm", &vtpm) || + grub_ieee1275_get_property (vtpm, "compatible", buffer, sizeof (buffer), NULL) || + grub_strcmp (buffer, "IBM,vtpm20")) { + if (grub_ieee1275_tpm_ihandle != GRUB_IEEE1275_IHANDLE_INVALID) + grub_ieee1275_close (grub_ieee1275_tpm_ihandle); + grub_ieee1275_tpm_ihandle = GRUB_IEEE1275_IHANDLE_INVALID; - return GRUB_ERR_UNKNOWN_DEVICE; } - - init_success = 1; - - tpm_get_tpm_version (); } + if (grub_ieee1275_tpm_ihandle == GRUB_IEEE1275_IHANDLE_INVALID) + return GRUB_ERR_UNKNOWN_DEVICE; + return GRUB_ERR_NONE; } diff --git a/include/grub/ieee1275/tpm.h b/include/grub/ieee1275/tpm.h index 9575c1c68..fe5cb4713 100644 --- a/include/grub/ieee1275/tpm.h +++ b/include/grub/ieee1275/tpm.h @@ -24,7 +24,6 @@ #include extern grub_ieee1275_ihandle_t grub_ieee1275_tpm_ihandle; -extern grub_uint8_t grub_ieee1275_tpm_version; extern grub_err_t grub_ieee1275_tpm_init (void); From ff14b89bda0445b97ad43b7cdbc5f20345f20006 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Tue, 26 Nov 2024 15:39:45 -0500 Subject: [PATCH 248/402] ieee1275/tcg2: Add TCG2 driver for ieee1275 PowerPC firmware Follow recent extensions of EFI support providing a TCG2 driver with a public API for getting the maximum TPM command size and passing a TPM command through to the TPM 2. Implement this functionality using ieee1275 PowerPC firmware API calls. Move tcg2.c into the TCG2 driver. Signed-off-by: Stefan Berger Reviewed-by: Daniel Kiper --- grub-core/Makefile.core.def | 3 +- grub-core/lib/ieee1275/tcg2.c | 102 ++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index c5fd796d4..8ecedf986 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1155,7 +1155,6 @@ module = { name = tpm; common = commands/tpm.c; ieee1275 = commands/ieee1275/ibmvtpm.c; - ieee1275 = lib/ieee1275/tcg2.c; enable = powerpc_ieee1275; }; @@ -2576,8 +2575,10 @@ module = { common = lib/tss2/tss2.c; efi = lib/efi/tcg2.c; emu = lib/tss2/tcg2_emu.c; + powerpc_ieee1275 = lib/ieee1275/tcg2.c; enable = efi; enable = emu; + enable = powerpc_ieee1275; cppflags = '-I$(srcdir)/lib/tss2'; }; diff --git a/grub-core/lib/ieee1275/tcg2.c b/grub-core/lib/ieee1275/tcg2.c index ea01a30eb..40161c2f9 100644 --- a/grub-core/lib/ieee1275/tcg2.c +++ b/grub-core/lib/ieee1275/tcg2.c @@ -23,6 +23,8 @@ #include #include +#include + grub_ieee1275_ihandle_t grub_ieee1275_tpm_ihandle = GRUB_IEEE1275_IHANDLE_INVALID; grub_err_t @@ -53,3 +55,103 @@ grub_ieee1275_tpm_init (void) return GRUB_ERR_NONE; } + +grub_err_t +grub_tcg2_get_max_output_size (grub_size_t *size) +{ + struct tpm_get_maximum_cmd_size + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t catch_result; + grub_ieee1275_cell_t size; + }; + struct tpm_get_maximum_cmd_size args; + static bool error_displayed = false; + grub_err_t err; + + err = grub_ieee1275_tpm_init (); + if (err != GRUB_ERR_NONE) + return err; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 2, 2); + args.method = (grub_ieee1275_cell_t) "get-maximum-cmd-size"; + args.ihandle = grub_ieee1275_tpm_ihandle; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return GRUB_ERR_INVALID_COMMAND; + + /* + * args.catch_result is set if firmware does not support get-maximum-cmd-size. + * rc is GRUB_IEEE1275_CELL_FALSE (0) on failure. + */ + if (args.catch_result) + { + if (error_displayed == false) + { + error_displayed = true; + return grub_error (GRUB_ERR_BAD_DEVICE, + "get-maximum-cmd-size failed: Firmware is likely too old"); + } + return GRUB_ERR_INVALID_COMMAND; + } + + *size = args.size; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_tcg2_submit_command (grub_size_t input_size, + grub_uint8_t *input, + grub_size_t output_size, + grub_uint8_t *output) +{ + struct tpm_pass_through_to_tpm + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t buf_size; + grub_ieee1275_cell_t buf_addr; + grub_ieee1275_cell_t catch_result; + grub_ieee1275_cell_t resp_size; + }; + struct tpm_pass_through_to_tpm args; + static bool error_displayed = false; + grub_err_t err; + + if (input_size == 0 || input == NULL || + output_size == 0 || output == NULL) + return GRUB_ERR_BAD_ARGUMENT; + + err = grub_ieee1275_tpm_init (); + if (err != GRUB_ERR_NONE) + return err; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 4, 2); + args.method = (grub_ieee1275_cell_t) "pass-through-to-tpm"; + args.ihandle = grub_ieee1275_tpm_ihandle; + args.buf_size = (grub_ieee1275_cell_t) input_size; + args.buf_addr = (grub_ieee1275_cell_t) input; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return GRUB_ERR_INVALID_COMMAND; + + /* args.catch_result is set if firmware does not support pass-through-to-tpm. */ + if (args.catch_result) + { + if (error_displayed == false) + { + error_displayed = true; + return grub_error (GRUB_ERR_BAD_DEVICE, + "pass-through-to-tpm failed: Firmware is likely too old"); + } + return GRUB_ERR_INVALID_COMMAND; + } + + grub_memcpy (output, input, args.resp_size); + + return GRUB_ERR_NONE; +} From 6811f6f09d61996a3acbc4fc0414e45964f0e2d9 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Tue, 26 Nov 2024 15:39:46 -0500 Subject: [PATCH 249/402] tpm2_key_protector: Enable build for powerpc_ieee1275 Signed-off-by: Stefan Berger Reviewed-by: Daniel Kiper --- grub-core/Makefile.core.def | 1 + 1 file changed, 1 insertion(+) diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 8ecedf986..f70e02e69 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -2591,6 +2591,7 @@ module = { /* The plaform support of tpm2_key_protector depends on the tcg2 implementation in tss2. */ enable = efi; enable = emu; + enable = powerpc_ieee1275; cppflags = '-I$(srcdir)/lib/tss2 -I$(srcdir)/lib/libtasn1-grub'; }; From ea703528a8581a2ea7e0bad424a70fdf0aec7d8f Mon Sep 17 00:00:00 2001 From: B Horn Date: Sat, 15 Jun 2024 02:33:08 +0100 Subject: [PATCH 250/402] misc: Implement grub_strlcpy() grub_strlcpy() acts the same way as strlcpy() does on most *NIX, returning the length of src and ensuring dest is always NUL terminated except when size is 0. Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- include/grub/misc.h | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/include/grub/misc.h b/include/grub/misc.h index 1578f36c3..14d8f37ac 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -64,6 +64,45 @@ grub_stpcpy (char *dest, const char *src) return d - 1; } +static inline grub_size_t +grub_strlcpy (char *dest, const char *src, grub_size_t size) +{ + char *d = dest; + grub_size_t res = 0; + /* + * We do not subtract one from size here to avoid dealing with underflowing + * the value, which is why to_copy is always checked to be greater than one + * throughout this function. + */ + grub_size_t to_copy = size; + + /* Copy size - 1 bytes to dest. */ + if (to_copy > 1) + while ((*d++ = *src++) != '\0' && ++res && --to_copy > 1) + ; + + /* + * NUL terminate if size != 0. The previous step may have copied a NUL byte + * if it reached the end of the string, but we know dest[size - 1] must always + * be a NUL byte. + */ + if (size != 0) + dest[size - 1] = '\0'; + + /* If there is still space in dest, but are here, we reached the end of src. */ + if (to_copy > 1) + return res; + + /* + * If we haven't reached the end of the string, iterate through to determine + * the strings total length. + */ + while (*src++ != '\0' && ++res) + ; + + 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) From c1a291b01f4f1dcd6a22b61f1c81a45a966d16ba Mon Sep 17 00:00:00 2001 From: B Horn Date: Sun, 12 May 2024 02:03:33 +0100 Subject: [PATCH 251/402] fs/ufs: Fix a heap OOB write grub_strcpy() was used to copy a symlink name from the filesystem image to a heap allocated buffer. This led to a OOB write to adjacent heap allocations. Fix by using grub_strlcpy(). Fixes: CVE-2024-45781 Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/fs/ufs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/fs/ufs.c b/grub-core/fs/ufs.c index a354c92d9..01235101b 100644 --- a/grub-core/fs/ufs.c +++ b/grub-core/fs/ufs.c @@ -463,7 +463,7 @@ grub_ufs_lookup_symlink (struct grub_ufs_data *data, int ino) /* Check against zero is paylindromic, no need to swap. */ if (data->inode.nblocks == 0 && INODE_SIZE (data) <= sizeof (data->inode.symlink)) - grub_strcpy (symlink, (char *) data->inode.symlink); + grub_strlcpy (symlink, (char *) data->inode.symlink, sz); else { if (grub_ufs_read_file (data, 0, 0, 0, sz, symlink) < 0) From 417547c10410b714e43f08f74137c24015f8f4c3 Mon Sep 17 00:00:00 2001 From: B Horn Date: Sun, 12 May 2024 02:48:33 +0100 Subject: [PATCH 252/402] fs/hfs: Fix stack OOB write with grub_strcpy() Replaced with grub_strlcpy(). Fixes: CVE-2024-45782 Fixes: CVE-2024-56737 Fixes: https://savannah.gnu.org/bugs/?66599 Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/fs/hfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/fs/hfs.c b/grub-core/fs/hfs.c index 91dc0e69c..920112b03 100644 --- a/grub-core/fs/hfs.c +++ b/grub-core/fs/hfs.c @@ -379,7 +379,7 @@ grub_hfs_mount (grub_disk_t disk) volume name. */ key.parent_dir = grub_cpu_to_be32_compile_time (1); key.strlen = data->sblock.volname[0]; - grub_strcpy ((char *) key.str, (char *) (data->sblock.volname + 1)); + grub_strlcpy ((char *) key.str, (char *) (data->sblock.volname + 1), sizeof (key.str)); if (grub_hfs_find_node (data, (char *) &key, data->cat_root, 0, (char *) &dir, sizeof (dir)) == 0) From 2c8ac08c99466c0697f704242363fc687f492a0d Mon Sep 17 00:00:00 2001 From: B Horn Date: Sun, 12 May 2024 02:47:54 +0100 Subject: [PATCH 253/402] fs/tar: Initialize name in grub_cpio_find_file() It was possible to iterate through grub_cpio_find_file() without allocating name and not setting mode to GRUB_ARCHELP_ATTR_END, which would cause the uninitialized value for name to be used as an argument for canonicalize() in grub_archelp_dir(). Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/fs/tar.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/grub-core/fs/tar.c b/grub-core/fs/tar.c index c551ed6b5..646bce5eb 100644 --- a/grub-core/fs/tar.c +++ b/grub-core/fs/tar.c @@ -78,6 +78,7 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name, int reread = 0, have_longname = 0, have_longlink = 0; data->hofs = data->next_hofs; + *name = NULL; for (reread = 0; reread < 3; reread++) { @@ -202,6 +203,10 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name, } return GRUB_ERR_NONE; } + + if (*name == NULL) + return grub_error (GRUB_ERR_BAD_FS, "invalid tar archive"); + return GRUB_ERR_NONE; } From 0087bc6902182fe5cedce2d034c75a79cf6dd4f3 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Fri, 22 Nov 2024 06:27:58 +0000 Subject: [PATCH 254/402] fs/tar: Integer overflow leads to heap OOB write Both namesize and linksize are derived from hd.size, a 12-digit octal number parsed by read_number(). Later direct arithmetic calculation like "namesize + 1" and "linksize + 1" may exceed the maximum value of grub_size_t leading to heap OOB write. This patch fixes the issue by using grub_add() and checking for an overflow. Fixes: CVE-2024-45780 Reported-by: Nils Langius Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper Reviewed-by: Alec Brown --- grub-core/fs/tar.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/grub-core/fs/tar.c b/grub-core/fs/tar.c index 646bce5eb..386c09022 100644 --- a/grub-core/fs/tar.c +++ b/grub-core/fs/tar.c @@ -25,6 +25,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -76,6 +77,7 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name, { struct head hd; int reread = 0, have_longname = 0, have_longlink = 0; + grub_size_t sz; data->hofs = data->next_hofs; *name = NULL; @@ -98,7 +100,11 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name, { grub_err_t err; grub_size_t namesize = read_number (hd.size, sizeof (hd.size)); - *name = grub_malloc (namesize + 1); + + if (grub_add (namesize, 1, &sz)) + return grub_error (GRUB_ERR_BAD_FS, N_("name size overflow")); + + *name = grub_malloc (sz); if (*name == NULL) return grub_errno; err = grub_disk_read (data->disk, 0, @@ -118,15 +124,19 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name, { grub_err_t err; grub_size_t linksize = read_number (hd.size, sizeof (hd.size)); - if (data->linkname_alloc < linksize + 1) + + if (grub_add (linksize, 1, &sz)) + return grub_error (GRUB_ERR_BAD_FS, N_("link size overflow")); + + if (data->linkname_alloc < sz) { char *n; - n = grub_calloc (2, linksize + 1); + n = grub_calloc (2, sz); if (!n) return grub_errno; grub_free (data->linkname); data->linkname = n; - data->linkname_alloc = 2 * (linksize + 1); + data->linkname_alloc = 2 * (sz); } err = grub_disk_read (data->disk, 0, @@ -149,7 +159,10 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name, while (extra_size < sizeof (hd.prefix) && hd.prefix[extra_size]) extra_size++; - *name = grub_malloc (sizeof (hd.name) + extra_size + 2); + + if (grub_add (sizeof (hd.name) + 2, extra_size, &sz)) + return grub_error (GRUB_ERR_BAD_FS, N_("long name size overflow")); + *name = grub_malloc (sz); if (*name == NULL) return grub_errno; if (hd.prefix[0]) From 563436258cde64da6b974880abff1bf0959f4da3 Mon Sep 17 00:00:00 2001 From: B Horn Date: Sun, 12 May 2024 06:15:03 +0100 Subject: [PATCH 255/402] fs/f2fs: Set a grub_errno if mount fails It was previously possible for grub_errno to not be set when grub_f2fs_mount() failed if nat_bitmap_ptr() returned NULL. This issue is solved by ensuring a grub_errno is set in the fail case. Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/fs/f2fs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/fs/f2fs.c b/grub-core/fs/f2fs.c index 855e24618..db8a65f8d 100644 --- a/grub-core/fs/f2fs.c +++ b/grub-core/fs/f2fs.c @@ -872,6 +872,9 @@ grub_f2fs_mount (grub_disk_t disk) return data; fail: + if (grub_errno == GRUB_ERR_NONE) + grub_error (GRUB_ERR_BAD_FS, "not a F2FS filesystem"); + grub_free (data); return NULL; From f7c070a2e28dfab7137db0739fb8db1dc02d8898 Mon Sep 17 00:00:00 2001 From: B Horn Date: Sun, 12 May 2024 06:22:51 +0100 Subject: [PATCH 256/402] fs/hfsplus: Set a grub_errno if mount fails It was possible for mount to fail but not set grub_errno. This led to a possible double decrement of the module reference count if the NULL page was mapped. Fixing in general as a similar bug was fixed in commit 61b13c187 (fs/hfsplus: Set grub_errno to prevent NULL pointer access) and there are likely more variants around. Fixes: CVE-2024-45783 Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/fs/hfsplus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/fs/hfsplus.c b/grub-core/fs/hfsplus.c index 295822f69..de71fd486 100644 --- a/grub-core/fs/hfsplus.c +++ b/grub-core/fs/hfsplus.c @@ -405,7 +405,7 @@ grub_hfsplus_mount (grub_disk_t disk) fail: - if (grub_errno == GRUB_ERR_OUT_OF_RANGE) + if (grub_errno == GRUB_ERR_OUT_OF_RANGE || grub_errno == GRUB_ERR_NONE) grub_error (GRUB_ERR_BAD_FS, "not a HFS+ filesystem"); grub_free (data); From 965db5970811d18069b34f28f5f31ddadde90a97 Mon Sep 17 00:00:00 2001 From: B Horn Date: Sun, 12 May 2024 06:37:08 +0100 Subject: [PATCH 257/402] fs/iso9660: Set a grub_errno if mount fails It was possible for a grub_errno to not be set if mount of an ISO 9660 filesystem failed when set_rockridge() returned 0. This isn't known to be exploitable as the other filesystems due to filesystem helper checking the requested file type. Though fixing as a precaution. Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/fs/iso9660.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/fs/iso9660.c b/grub-core/fs/iso9660.c index 8c348b59a..8d480e602 100644 --- a/grub-core/fs/iso9660.c +++ b/grub-core/fs/iso9660.c @@ -551,6 +551,9 @@ grub_iso9660_mount (grub_disk_t disk) return data; fail: + if (grub_errno == GRUB_ERR_NONE) + grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem"); + grub_free (data); return 0; } From 1443833a9535a5873f7de3798cf4d8389f366611 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Fri, 31 May 2024 15:14:42 +0800 Subject: [PATCH 258/402] fs/iso9660: Fix invalid free The ctx->filename can point to either a string literal or a dynamically allocated string. The ctx->filename_alloc field is used to indicate the type of allocation. An issue has been identified where ctx->filename is reassigned to a string literal in susp_iterate_dir() but ctx->filename_alloc is not correctly handled. This oversight causes a memory leak and an invalid free operation later. The fix involves checking ctx->filename_alloc, freeing the allocated string if necessary and clearing ctx->filename_alloc for string literals. Reported-by: Daniel Axtens Signed-off-by: Michael Chang Reviewed-by: Daniel Kiper --- grub-core/fs/iso9660.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/grub-core/fs/iso9660.c b/grub-core/fs/iso9660.c index 8d480e602..8e3c95c4f 100644 --- a/grub-core/fs/iso9660.c +++ b/grub-core/fs/iso9660.c @@ -628,9 +628,19 @@ susp_iterate_dir (struct grub_iso9660_susp_entry *entry, filename type is stored. */ /* FIXME: Fix this slightly improper cast. */ if (entry->data[0] & GRUB_ISO9660_RR_DOT) - ctx->filename = (char *) "."; + { + if (ctx->filename_alloc) + grub_free (ctx->filename); + ctx->filename_alloc = 0; + ctx->filename = (char *) "."; + } else if (entry->data[0] & GRUB_ISO9660_RR_DOTDOT) - ctx->filename = (char *) ".."; + { + if (ctx->filename_alloc) + grub_free (ctx->filename); + ctx->filename_alloc = 0; + ctx->filename = (char *) ".."; + } else if (entry->len >= 5) { grub_size_t off = 0, csize = 1; From 66175696f3a385b14bdf1ebcda7755834bd2d5fb Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Fri, 22 Nov 2024 06:27:59 +0000 Subject: [PATCH 259/402] fs/jfs: Fix OOB read in jfs_getent() The JFS fuzzing revealed an OOB read in grub_jfs_getent(). The crash was caused by an invalid leaf nodes count, diro->dirpage->header.count, which was larger than the maximum number of leaf nodes allowed in an inode. This fix is to ensure that the leaf nodes count is validated in grub_jfs_opendir() before calling grub_jfs_getent(). On the occasion replace existing raw numbers with newly defined constant. Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper Reviewed-by: Alec Brown --- grub-core/fs/jfs.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/grub-core/fs/jfs.c b/grub-core/fs/jfs.c index 62e20ef6f..e2fe2850c 100644 --- a/grub-core/fs/jfs.c +++ b/grub-core/fs/jfs.c @@ -41,6 +41,12 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define GRUB_JFS_TREE_LEAF 2 +/* + * Define max entries stored in-line in an inode. + * https://jfs.sourceforge.net/project/pub/jfslayout.pdf + */ +#define GRUB_JFS_INODE_INLINE_ENTRIES 8 + struct grub_jfs_sblock { /* The magic for JFS. It should contain the string "JFS1". */ @@ -203,9 +209,9 @@ struct grub_jfs_inode grub_uint8_t freecnt; grub_uint8_t freelist; grub_uint32_t idotdot; - grub_uint8_t sorted[8]; + grub_uint8_t sorted[GRUB_JFS_INODE_INLINE_ENTRIES]; } header; - struct grub_jfs_leaf_dirent dirents[8]; + struct grub_jfs_leaf_dirent dirents[GRUB_JFS_INODE_INLINE_ENTRIES]; } GRUB_PACKED dir; /* Fast symlink. */ struct @@ -453,6 +459,13 @@ grub_jfs_opendir (struct grub_jfs_data *data, struct grub_jfs_inode *inode) /* Check if the entire tree is contained within the inode. */ if (inode->file.tree.flags & GRUB_JFS_TREE_LEAF) { + if (inode->dir.header.count > GRUB_JFS_INODE_INLINE_ENTRIES) + { + grub_free (diro); + grub_error (GRUB_ERR_BAD_FS, N_("invalid JFS inode")); + return 0; + } + diro->leaf = inode->dir.dirents; diro->next_leaf = (struct grub_jfs_leaf_next_dirent *) de; diro->sorted = inode->dir.header.sorted; From ab09fd0531f3523ac0ef833404526c98c08248f7 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Fri, 22 Nov 2024 06:28:00 +0000 Subject: [PATCH 260/402] fs/jfs: Fix OOB read caused by invalid dir slot index While fuzz testing JFS with ASAN enabled an OOB read was detected in grub_jfs_opendir(). The issue occurred due to an invalid directory slot index in the first entry of the sorted directory slot array in the inode directory header. The fix ensures the slot index is validated before accessing it. Given that an internal or a leaf node in a directory B+ tree is a 4 KiB in size and each directory slot is always 32 bytes, the max number of slots in a node is 128. The validation ensures that the slot index doesn't exceed this limit. [1] https://jfs.sourceforge.net/project/pub/jfslayout.pdf JFS will allocate 4K of disk space for an internal node of the B+ tree. An internal node looks the same as a leaf node. - page 10 Fixed number of Directory Slots depending on the size of the node. These are the slots to be used for storing the directory slot array and the directory entries or router entries. A directory slot is always 32 bytes. ... A Directory Slot Array which is a sorted array of indices to the directory slots that are currently in use. ... An internal or a leaf node in the directory B+ tree is a 4K page. - page 25 Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper Reviewed-by: Alec Brown --- grub-core/fs/jfs.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/grub-core/fs/jfs.c b/grub-core/fs/jfs.c index e2fe2850c..7a68fcbe3 100644 --- a/grub-core/fs/jfs.c +++ b/grub-core/fs/jfs.c @@ -46,6 +46,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); * https://jfs.sourceforge.net/project/pub/jfslayout.pdf */ #define GRUB_JFS_INODE_INLINE_ENTRIES 8 +#define GRUB_JFS_DIR_MAX_SLOTS 128 struct grub_jfs_sblock { @@ -481,6 +482,14 @@ grub_jfs_opendir (struct grub_jfs_data *data, struct grub_jfs_inode *inode) return 0; } + if (inode->dir.header.sorted[0] >= GRUB_JFS_DIR_MAX_SLOTS) + { + grub_error (GRUB_ERR_BAD_FS, N_("invalid directory slot index")); + grub_free (diro->dirpage); + grub_free (diro); + return 0; + } + blk = grub_le_to_cpu32 (de[inode->dir.header.sorted[0]].ex.blk2); blk <<= (grub_le_to_cpu16 (data->sblock.log2_blksz) - GRUB_DISK_SECTOR_BITS); From bd999310fe67f35a66de3bfa2836da91589d04ef Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Mon, 16 Dec 2024 20:22:39 +0000 Subject: [PATCH 261/402] fs/jfs: Use full 40 bits offset and address for a data extent An extent's logical offset and address are represented as a 40-bit value split into two parts: the most significant 8 bits and the least significant 32 bits. Currently the JFS code uses only the least significant 32 bits value for offsets and addresses assuming the data size will never exceed the 32-bit range. This approach ignores the most significant 8 bits potentially leading to incorrect offsets and addresses for larger values. The patch fixes it by incorporating the most significant 8 bits into the calculation to get the full 40-bits value for offsets and addresses. https://jfs.sourceforge.net/project/pub/jfslayout.pdf "off1,off2 is a 40-bit field, containing the logical offset of the first block in the extent. ... addr1,addr2 is a 40-bit field, containing the address of the extent." Signed-off-by: Lidong Chen Reviewed-by: Alec Brown Reviewed-by: Ross Philipson Reviewed-by: Daniel Kiper --- grub-core/fs/jfs.c | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/grub-core/fs/jfs.c b/grub-core/fs/jfs.c index 7a68fcbe3..3026d5a0b 100644 --- a/grub-core/fs/jfs.c +++ b/grub-core/fs/jfs.c @@ -265,6 +265,20 @@ static grub_dl_t my_mod; static grub_err_t grub_jfs_lookup_symlink (struct grub_jfs_data *data, grub_uint32_t ino); +/* + * An extent's offset, physical and logical, is represented as a 40-bit value. + * This 40-bit value is split into two parts: + * - offset1: the most signficant 8 bits of the offset, + * - offset2: the least significant 32 bits of the offset. + * + * This function calculates and returns the 64-bit offset of an extent. + */ +static grub_uint64_t +get_ext_offset (grub_uint8_t offset1, grub_uint32_t offset2) +{ + return (((grub_uint64_t) offset1 << 32) | grub_le_to_cpu32 (offset2)); +} + static grub_int64_t getblk (struct grub_jfs_treehead *treehead, struct grub_jfs_tree_extent *extents, @@ -274,22 +288,25 @@ getblk (struct grub_jfs_treehead *treehead, { int found = -1; int i; + grub_uint64_t ext_offset, ext_blk; for (i = 0; i < grub_le_to_cpu16 (treehead->count) - 2 && i < max_extents; i++) { + ext_offset = get_ext_offset (extents[i].offset1, extents[i].offset2); + ext_blk = get_ext_offset (extents[i].extent.blk1, extents[i].extent.blk2); + if (treehead->flags & GRUB_JFS_TREE_LEAF) { /* Read the leafnode. */ - if (grub_le_to_cpu32 (extents[i].offset2) <= blk + if (ext_offset <= blk && ((grub_le_to_cpu16 (extents[i].extent.length)) + (extents[i].extent.length2 << 16) - + grub_le_to_cpu32 (extents[i].offset2)) > blk) - return (blk - grub_le_to_cpu32 (extents[i].offset2) - + grub_le_to_cpu32 (extents[i].extent.blk2)); + + ext_offset) > blk) + return (blk - ext_offset + ext_blk); } else - if (blk >= grub_le_to_cpu32 (extents[i].offset2)) + if (blk >= ext_offset) found = i; } @@ -307,10 +324,9 @@ getblk (struct grub_jfs_treehead *treehead, return -1; if (!grub_disk_read (data->disk, - ((grub_disk_addr_t) grub_le_to_cpu32 (extents[found].extent.blk2)) - << (grub_le_to_cpu16 (data->sblock.log2_blksz) - - GRUB_DISK_SECTOR_BITS), 0, - sizeof (*tree), (char *) tree)) + (grub_disk_addr_t) ext_blk + << (grub_le_to_cpu16 (data->sblock.log2_blksz) - GRUB_DISK_SECTOR_BITS), + 0, sizeof (*tree), (char *) tree)) { if (grub_memcmp (&tree->treehead, treehead, sizeof (struct grub_jfs_treehead)) || grub_memcmp (&tree->extents, extents, 254 * sizeof (struct grub_jfs_tree_extent))) @@ -361,7 +377,7 @@ grub_jfs_read_inode (struct grub_jfs_data *data, grub_uint32_t ino, sizeof (iag_inodes), &iag_inodes)) return grub_errno; - inoblk = grub_le_to_cpu32 (iag_inodes[inoext].blk2); + inoblk = get_ext_offset (iag_inodes[inoext].blk1, iag_inodes[inoext].blk2); inoblk <<= (grub_le_to_cpu16 (data->sblock.log2_blksz) - GRUB_DISK_SECTOR_BITS); inoblk += inonum; @@ -490,7 +506,8 @@ grub_jfs_opendir (struct grub_jfs_data *data, struct grub_jfs_inode *inode) return 0; } - blk = grub_le_to_cpu32 (de[inode->dir.header.sorted[0]].ex.blk2); + blk = get_ext_offset (de[inode->dir.header.sorted[0]].ex.blk1, + de[inode->dir.header.sorted[0]].ex.blk2); blk <<= (grub_le_to_cpu16 (data->sblock.log2_blksz) - GRUB_DISK_SECTOR_BITS); /* Read in the nodes until we are on the leaf node level. */ @@ -508,7 +525,7 @@ grub_jfs_opendir (struct grub_jfs_data *data, struct grub_jfs_inode *inode) de = (struct grub_jfs_internal_dirent *) diro->dirpage->dirent; index = diro->dirpage->sorted[diro->dirpage->header.sindex * 32]; - blk = (grub_le_to_cpu32 (de[index].ex.blk2) + blk = (get_ext_offset (de[index].ex.blk1, de[index].ex.blk2) << (grub_le_to_cpu16 (data->sblock.log2_blksz) - GRUB_DISK_SECTOR_BITS)); } while (!(diro->dirpage->header.flags & GRUB_JFS_TREE_LEAF)); From edd995a26ec98654d907a9436a296c2d82bc4b28 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Mon, 16 Dec 2024 20:22:40 +0000 Subject: [PATCH 262/402] fs/jfs: Inconsistent signed/unsigned types usage in return values The getblk() returns a value of type grub_int64_t which is assigned to iagblk and inoblk, both of type grub_uint64_t, in grub_jfs_read_inode() via grub_jfs_blkno(). This patch fixes the type mismatch in the functions. Additionally, the getblk() will return 0 instead of -1 on failure cases. This change is safe because grub_errno is always set in getblk() to indicate errors and it is later checked in the callers. Signed-off-by: Lidong Chen Reviewed-by: Alec Brown Reviewed-by: Ross Philipson Reviewed-by: Daniel Kiper --- grub-core/fs/jfs.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/grub-core/fs/jfs.c b/grub-core/fs/jfs.c index 3026d5a0b..c06d174c9 100644 --- a/grub-core/fs/jfs.c +++ b/grub-core/fs/jfs.c @@ -279,7 +279,7 @@ get_ext_offset (grub_uint8_t offset1, grub_uint32_t offset2) return (((grub_uint64_t) offset1 << 32) | grub_le_to_cpu32 (offset2)); } -static grub_int64_t +static grub_uint64_t getblk (struct grub_jfs_treehead *treehead, struct grub_jfs_tree_extent *extents, int max_extents, @@ -290,6 +290,8 @@ getblk (struct grub_jfs_treehead *treehead, int i; grub_uint64_t ext_offset, ext_blk; + grub_errno = GRUB_ERR_NONE; + for (i = 0; i < grub_le_to_cpu16 (treehead->count) - 2 && i < max_extents; i++) { @@ -312,7 +314,7 @@ getblk (struct grub_jfs_treehead *treehead, if (found != -1) { - grub_int64_t ret = -1; + grub_uint64_t ret = 0; struct { struct grub_jfs_treehead treehead; @@ -321,7 +323,7 @@ getblk (struct grub_jfs_treehead *treehead, tree = grub_zalloc (sizeof (*tree)); if (!tree) - return -1; + return 0; if (!grub_disk_read (data->disk, (grub_disk_addr_t) ext_blk @@ -334,19 +336,20 @@ getblk (struct grub_jfs_treehead *treehead, else { grub_error (GRUB_ERR_BAD_FS, "jfs: infinite recursion detected"); - ret = -1; + ret = 0; } } grub_free (tree); return ret; } - return -1; + grub_error (GRUB_ERR_READ_ERROR, "jfs: block %" PRIuGRUB_UINT64_T " not found", blk); + return 0; } /* Get the block number for the block BLK in the node INODE in the mounted filesystem DATA. */ -static grub_int64_t +static grub_uint64_t grub_jfs_blkno (struct grub_jfs_data *data, struct grub_jfs_inode *inode, grub_uint64_t blk) { From 7e2f750f0a795c4d64ec7dc7591edac8da2e978c Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Fri, 31 May 2024 15:14:23 +0800 Subject: [PATCH 263/402] fs/ext2: Fix out-of-bounds read for inline extents When inline extents are used, i.e. the extent tree depth equals zero, a maximum of four entries can fit into the inode's data block. If the extent header states a number of entries greater than four the current ext2 implementation causes an out-of-bounds read. Fix this issue by capping the number of extents to four when reading inline extents. Reported-by: Daniel Axtens Signed-off-by: Michael Chang Reviewed-by: Daniel Kiper --- grub-core/fs/ext2.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/grub-core/fs/ext2.c b/grub-core/fs/ext2.c index e1cc5e62a..3f9f6b208 100644 --- a/grub-core/fs/ext2.c +++ b/grub-core/fs/ext2.c @@ -495,6 +495,8 @@ grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) struct grub_ext4_extent *ext; int i; grub_disk_addr_t ret; + grub_uint16_t nent; + const grub_uint16_t max_inline_ext = sizeof (inode->blocks) / sizeof (*ext) - 1; /* Minus 1 extent header. */ if (grub_ext4_find_leaf (data, (struct grub_ext4_extent_header *) inode->blocks.dir_blocks, fileblock, &leaf) != GRUB_ERR_NONE) @@ -508,7 +510,13 @@ grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) return 0; ext = (struct grub_ext4_extent *) (leaf + 1); - for (i = 0; i < grub_le_to_cpu16 (leaf->entries); i++) + + nent = grub_le_to_cpu16 (leaf->entries); + + if (leaf->depth == 0) + nent = grub_min (nent, max_inline_ext); + + for (i = 0; i < nent; i++) { if (fileblock < grub_le_to_cpu32 (ext[i].block)) break; From aff26318783a135562b904ff09e2359893885732 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Mon, 3 Jun 2024 12:12:06 +0800 Subject: [PATCH 264/402] fs/ntfs: Fix out-of-bounds read When parsing NTFS file records the presence of the 0xFF marker indicates the end of the attribute list. This value signifies that there are no more attributes to process. However, when the end marker is missing due to corrupted metadata the loop continues to read beyond the attribute list resulting in out-of-bounds reads and potentially entering an infinite loop. This patch adds a check to provide a stop condition for the loop ensuring it stops at the end of the attribute list or at the end of the Master File Table. This guards against out-of-bounds reads and prevents infinite loops. Reported-by: Daniel Axtens Signed-off-by: Michael Chang Reviewed-by: Daniel Kiper --- grub-core/fs/ntfs.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index de435aa14..8a5384247 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -139,6 +139,8 @@ free_attr (struct grub_ntfs_attr *at) static grub_uint8_t * find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) { + grub_uint8_t *mft_end; + if (at->flags & GRUB_NTFS_AF_ALST) { retry: @@ -191,7 +193,8 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) return NULL; } at->attr_cur = at->attr_nxt; - while (*at->attr_cur != 0xFF) + mft_end = at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR); + while (at->attr_cur < mft_end && *at->attr_cur != 0xFF) { at->attr_nxt += u16at (at->attr_cur, 4); if (*at->attr_cur == GRUB_NTFS_AT_ATTRIBUTE_LIST) From 237a71184a32d1ef7732f5f49ed6a89c5fe1c99a Mon Sep 17 00:00:00 2001 From: B Horn Date: Tue, 7 Jan 2025 11:38:34 +0000 Subject: [PATCH 265/402] fs/ntfs: Track the end of the MFT attribute buffer The end of the attribute buffer should be stored alongside the rest of the attribute struct as right now it is not possible to implement bounds checking when accessing attributes sequentially. This is done via: - updating init_attr() to set at->end and check is is not initially out of bounds, - implementing checks as init_attr() had its type change in its callers, - updating the value of at->end when needed. Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/fs/ntfs.c | 34 ++++++++++++++++++++++++++++------ include/grub/ntfs.h | 1 + 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index 8a5384247..dbda720e1 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -119,13 +119,20 @@ static grub_err_t read_data (struct grub_ntfs_attr *at, grub_uint8_t *pa, grub_disk_read_hook_t read_hook, void *read_hook_data); -static void +static grub_err_t init_attr (struct grub_ntfs_attr *at, struct grub_ntfs_file *mft) { at->mft = mft; at->flags = (mft == &mft->data->mmft) ? GRUB_NTFS_AF_MMFT : 0; at->attr_nxt = mft->buf + first_attr_off (mft->buf); + at->end = mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR); + + if (at->attr_nxt > at->end) + return grub_error (GRUB_ERR_BAD_FS, "attributes start outside the MFT"); + at->attr_end = at->emft_buf = at->edat_buf = at->sbuf = NULL; + + return GRUB_ERR_NONE; } static void @@ -239,6 +246,10 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) pa_end = at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR); } at->flags |= GRUB_NTFS_AF_ALST; + + /* From this point on pa_end is the end of the buffer */ + at->end = pa_end; + while (at->attr_nxt < at->attr_end) { if ((*at->attr_nxt == attr) || (attr == 0)) @@ -298,7 +309,9 @@ locate_attr (struct grub_ntfs_attr *at, struct grub_ntfs_file *mft, { grub_uint8_t *pa; - init_attr (at, mft); + if (init_attr (at, mft) != GRUB_ERR_NONE) + return NULL; + pa = find_attr (at, attr); if (pa == NULL) return NULL; @@ -314,7 +327,8 @@ locate_attr (struct grub_ntfs_attr *at, struct grub_ntfs_file *mft, } grub_errno = GRUB_ERR_NONE; free_attr (at); - init_attr (at, mft); + if (init_attr (at, mft) != GRUB_ERR_NONE) + return NULL; pa = find_attr (at, attr); } return pa; @@ -585,7 +599,7 @@ init_file (struct grub_ntfs_file *mft, grub_uint64_t mftno) mft->attr.attr_end = 0; /* Don't jump to attribute list */ } else - init_attr (&mft->attr, mft); + return init_attr (&mft->attr, mft); return 0; } @@ -811,7 +825,9 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, bmp = NULL; at = &attr; - init_attr (at, mft); + if (init_attr (at, mft) != GRUB_ERR_NONE) + return 0; + while (1) { cur_pos = find_attr (at, GRUB_NTFS_AT_INDEX_ROOT); @@ -842,7 +858,9 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, bitmap = NULL; bitmap_len = 0; free_attr (at); + /* No need to check errors here, as it will already be fine */ init_attr (at, mft); + while ((cur_pos = find_attr (at, GRUB_NTFS_AT_BITMAP)) != NULL) { int ofs; @@ -1207,6 +1225,7 @@ grub_ntfs_label (grub_device_t device, char **label) struct grub_ntfs_data *data = 0; struct grub_fshelp_node *mft = 0; grub_uint8_t *pa; + grub_err_t err; grub_dl_ref (my_mod); @@ -1232,7 +1251,10 @@ grub_ntfs_label (grub_device_t device, char **label) goto fail; } - init_attr (&mft->attr, mft); + err = init_attr (&mft->attr, mft); + if (err != GRUB_ERR_NONE) + return err; + pa = find_attr (&mft->attr, GRUB_NTFS_AT_VOLUME_NAME); if (pa >= mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR)) diff --git a/include/grub/ntfs.h b/include/grub/ntfs.h index d1a6af696..ec1c4db38 100644 --- a/include/grub/ntfs.h +++ b/include/grub/ntfs.h @@ -134,6 +134,7 @@ struct grub_ntfs_attr grub_uint8_t *attr_cur, *attr_nxt, *attr_end; grub_uint32_t save_pos; grub_uint8_t *sbuf; + grub_uint8_t *end; struct grub_ntfs_file *mft; }; From 048777bc29043403d077d41a81d0183767b8bc71 Mon Sep 17 00:00:00 2001 From: B Horn Date: Tue, 14 May 2024 12:39:56 +0100 Subject: [PATCH 266/402] fs/ntfs: Use a helper function to access attributes Right now to access the next attribute the code reads the length of the current attribute and adds that to the current pointer. This is error prone as bounds checking needs to be performed all over the place. So, implement a helper and ensure its used across find_attr() and read_attr(). This commit does *not* implement full bounds checking. It is just the preparation work for this to be added into the helper. Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/fs/ntfs.c | 69 ++++++++++++++++++++++++++++++++++++--------- include/grub/ntfs.h | 2 ++ 2 files changed, 58 insertions(+), 13 deletions(-) diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index dbda720e1..1c678f3d0 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -70,6 +70,25 @@ res_attr_data_len (void *res_attr_ptr) return u32at (res_attr_ptr, 0x10); } +/* Return the next attribute if it exists, otherwise return NULL. */ +static grub_uint8_t * +next_attribute (grub_uint8_t *curr_attribute, void *end) +{ + grub_uint8_t *next = curr_attribute; + + /* + * Need to verify we aren't exceeding the end of the buffer by reading the + * header for the current attribute + */ + if (curr_attribute + GRUB_NTFS_ATTRIBUTE_HEADER_SIZE >= (grub_uint8_t *) end) + return NULL; + + next += u16at (curr_attribute, 4); + + return next; +} + + grub_ntfscomp_func_t grub_ntfscomp_func; static grub_err_t @@ -151,13 +170,13 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) if (at->flags & GRUB_NTFS_AF_ALST) { retry: - while (at->attr_nxt < at->attr_end) + while (at->attr_nxt) { at->attr_cur = at->attr_nxt; - at->attr_nxt += u16at (at->attr_cur, 4); + at->attr_nxt = next_attribute (at->attr_cur, at->attr_end); if ((*at->attr_cur == attr) || (attr == 0)) { - grub_uint8_t *new_pos; + grub_uint8_t *new_pos, *end; if (at->flags & GRUB_NTFS_AF_MMFT) { @@ -181,15 +200,36 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) return NULL; } + /* + * Only time emft_bufs is defined is in this function, with this + * size. + */ + grub_size_t emft_buf_size = + at->mft->data->mft_size << GRUB_NTFS_BLK_SHR; + + /* + * Needs to be enough space for the successful case to even + * bother. + */ + if (first_attr_off (at->emft_buf) >= (emft_buf_size - 0x18 - 2)) + { + grub_error (GRUB_ERR_BAD_FS, + "can\'t find 0x%X in attribute list", + (unsigned char) *at->attr_cur); + return NULL; + } + new_pos = &at->emft_buf[first_attr_off (at->emft_buf)]; - while (*new_pos != 0xFF) + end = &at->emft_buf[emft_buf_size]; + + while (new_pos && *new_pos != 0xFF) { if ((*new_pos == *at->attr_cur) && (u16at (new_pos, 0xE) == u16at (at->attr_cur, 0x18))) { return new_pos; } - new_pos += u16at (new_pos, 4); + new_pos = next_attribute (new_pos, end); } grub_error (GRUB_ERR_BAD_FS, "can\'t find 0x%X in attribute list", @@ -203,7 +243,7 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) mft_end = at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR); while (at->attr_cur < mft_end && *at->attr_cur != 0xFF) { - at->attr_nxt += u16at (at->attr_cur, 4); + at->attr_nxt = next_attribute (at->attr_cur, at->end); if (*at->attr_cur == GRUB_NTFS_AT_ATTRIBUTE_LIST) at->attr_end = at->attr_cur; if ((*at->attr_cur == attr) || (attr == 0)) @@ -250,13 +290,14 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) /* From this point on pa_end is the end of the buffer */ at->end = pa_end; - while (at->attr_nxt < at->attr_end) + while (at->attr_nxt) { if ((*at->attr_nxt == attr) || (attr == 0)) break; - at->attr_nxt += u16at (at->attr_nxt, 4); + at->attr_nxt = next_attribute (at->attr_nxt, pa_end); } - if (at->attr_nxt >= at->attr_end) + + if (at->attr_nxt >= at->attr_end || at->attr_nxt == NULL) return NULL; if ((at->flags & GRUB_NTFS_AF_MMFT) && (attr == GRUB_NTFS_AT_DATA)) @@ -277,7 +318,8 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) grub_cpu_to_le32 (at->mft->data->mft_start + 1)); pa = at->attr_nxt + u16at (pa, 4); - while (pa < at->attr_end) + + while (pa) { if (*pa != attr) break; @@ -293,7 +335,7 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) u32at (pa, 0x10) * (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR), at->mft->data->mft_size << GRUB_NTFS_BLK_SHR, 0, 0, 0)) return NULL; - pa += u16at (pa, 4); + pa = next_attribute (pa, pa_end); } at->attr_nxt = at->attr_cur; at->flags &= ~GRUB_NTFS_AF_GPOS; @@ -530,14 +572,15 @@ read_attr (struct grub_ntfs_attr *at, grub_uint8_t *dest, grub_disk_addr_t ofs, else vcn = ofs >> (at->mft->data->log_spc + GRUB_NTFS_BLK_SHR); pa = at->attr_nxt + u16at (at->attr_nxt, 4); - while (pa < at->attr_end) + + while (pa) { if (*pa != attr) break; if (u32at (pa, 8) > vcn) break; at->attr_nxt = pa; - pa += u16at (pa, 4); + pa = next_attribute (pa, at->attr_end); } } pp = find_attr (at, attr); diff --git a/include/grub/ntfs.h b/include/grub/ntfs.h index ec1c4db38..2c8078403 100644 --- a/include/grub/ntfs.h +++ b/include/grub/ntfs.h @@ -89,6 +89,8 @@ enum #define GRUB_NTFS_COM_SEC (GRUB_NTFS_COM_LEN >> GRUB_NTFS_BLK_SHR) #define GRUB_NTFS_LOG_COM_SEC (GRUB_NTFS_COM_LOG_LEN - GRUB_NTFS_BLK_SHR) +#define GRUB_NTFS_ATTRIBUTE_HEADER_SIZE 16 + enum { GRUB_NTFS_AF_ALST = 1, From 067b6d225d482280abad03944f04e30abcbdafa1 Mon Sep 17 00:00:00 2001 From: B Horn Date: Tue, 14 May 2024 12:39:56 +0100 Subject: [PATCH 267/402] fs/ntfs: Implement attribute verification It was possible to read OOB when an attribute had a size that exceeded the allocated buffer. This resolves that by making sure all attributes that get read are fully in the allocated space by implementing a function to validate them. Defining the offsets in include/grub/ntfs.h but they are only used in the validation function and not across the rest of the NTFS code. Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/fs/ntfs.c | 153 ++++++++++++++++++++++++++++++++++++++++++++ include/grub/ntfs.h | 22 +++++++ 2 files changed, 175 insertions(+) diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index 1c678f3d0..64f4f2221 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -70,6 +70,149 @@ res_attr_data_len (void *res_attr_ptr) return u32at (res_attr_ptr, 0x10); } +/* + * Check if the attribute is valid and doesn't exceed the allocated region. + * This accounts for resident and non-resident data. + * + * This is based off the documentation from the linux-ntfs project: + * https://flatcap.github.io/linux-ntfs/ntfs/concepts/attribute_header.html + */ +static bool +validate_attribute (grub_uint8_t *attr, void *end) +{ + grub_size_t attr_size = 0; + grub_size_t min_size = 0; + grub_size_t spare = (grub_uint8_t *) end - attr; + /* + * Just used as a temporary variable to try and deal with cases where someone + * tries to overlap fields. + */ + grub_size_t curr = 0; + + /* Need verify we can entirely read the attributes header. */ + if (attr + GRUB_NTFS_ATTRIBUTE_HEADER_SIZE >= (grub_uint8_t *) end) + goto fail; + + /* + * So, the rest of this code uses a 16bit int for the attribute length but + * from reading the all the documentation I could find it says this field is + * actually 32bit. But let's be consistent with the rest of the code. + * + * https://elixir.bootlin.com/linux/v6.10.7/source/fs/ntfs3/ntfs.h#L370 + */ + attr_size = u16at (attr, GRUB_NTFS_ATTRIBUTE_LENGTH); + + if (attr_size > spare) + goto fail; + + /* Not an error case, just reached the end of the attributes. */ + if (attr_size == 0) + return false; + + /* + * Extra validation by trying to calculate a minimum possible size for this + * attribute. +8 from the size of the resident data struct which is the + * minimum that can be added. + */ + min_size = GRUB_NTFS_ATTRIBUTE_HEADER_SIZE + 8; + + if (min_size > attr_size) + goto fail; + + /* Is the data is resident (0) or not (1). */ + if (attr[GRUB_NTFS_ATTRIBUTE_RESIDENT] == 0) + { + /* Read the offset and size of the attribute. */ + curr = u16at (attr, GRUB_NTFS_ATTRIBUTE_RES_OFFSET); + curr += u32at (attr, GRUB_NTFS_ATTRIBUTE_RES_LENGTH); + if (curr > min_size) + min_size = curr; + } + else + { + /* + * If the data is non-resident, the minimum size is 64 which is where + * the data runs start. We already have a minimum size of 24. So, just + * adding 40 to get to the real value. + */ + min_size += 40; + if (min_size > attr_size) + goto fail; + /* If the compression unit size is > 0, +8 bytes*/ + if (u16at (attr, GRUB_NTFS_ATTRIBUTE_COMPRESSION_UNIT_SIZE) > 0) + min_size += 8; + + /* + * Need to consider the data runs now. Each member of the run has byte + * that describes the size of the data length and offset. Each being + * 4 bits in the byte. + */ + curr = u16at (attr, GRUB_NTFS_ATTRIBUTE_DATA_RUNS); + + if (curr + 1 > min_size) + min_size = curr + 1; + + if (min_size > attr_size) + goto fail; + + /* + * Each attribute can store multiple data runs which are stored + * continuously in the attribute. They exist as one header byte + * with up to 14 bytes following it depending on the lengths. + * We stop when we hit a header that is just a NUL byte. + * + * https://flatcap.github.io/linux-ntfs/ntfs/concepts/data_runs.html + */ + while (attr[curr] != 0) + { + /* + * We stop when we hit a header that is just a NUL byte. The data + * run header is stored as a single byte where the top 4 bits refer + * to the number of bytes used to store the total length of the + * data run, and the number of bytes used to store the offset. + * These directly follow the header byte, so we use them to update + * the minimum size. + */ + min_size += (attr[curr] & 0x7) + ((attr[curr] >> 4) & 0x7); + curr += min_size; + min_size++; + if (min_size > attr_size) + goto fail; + } + } + + /* Name offset, doing this after data residence checks. */ + if (u16at (attr, GRUB_NTFS_ATTRIBUTE_NAME_OFFSET) != 0) + { + curr = u16at (attr, GRUB_NTFS_ATTRIBUTE_NAME_OFFSET); + /* + * Multiple the name length by 2 as its UTF-16. Can be zero if this in an + * unamed attribute. + */ + curr += attr[GRUB_NTFS_ATTRIBUTE_NAME_LENGTH] * 2; + if (curr > min_size) + min_size = curr; + } + + /* Padded to 8 bytes. */ + if (min_size % 8 != 0) + min_size += 8 - (min_size % 8); + + /* + * At this point min_size should be exactly attr_size but being flexible + * here to avoid any issues. + */ + if (min_size > attr_size) + goto fail; + + return true; + + fail: + grub_dprintf ("ntfs", "spare=%" PRIuGRUB_SIZE " min_size=%" PRIuGRUB_SIZE " attr_size=%" PRIuGRUB_SIZE "\n", + spare, min_size, attr_size); + return false; +} + /* Return the next attribute if it exists, otherwise return NULL. */ static grub_uint8_t * next_attribute (grub_uint8_t *curr_attribute, void *end) @@ -84,6 +227,8 @@ next_attribute (grub_uint8_t *curr_attribute, void *end) return NULL; next += u16at (curr_attribute, 4); + if (validate_attribute (next, end) == false) + return NULL; return next; } @@ -290,6 +435,9 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) /* From this point on pa_end is the end of the buffer */ at->end = pa_end; + if (validate_attribute (at->attr_nxt, pa_end) == false) + return NULL; + while (at->attr_nxt) { if ((*at->attr_nxt == attr) || (attr == 0)) @@ -319,6 +467,9 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) + 1)); pa = at->attr_nxt + u16at (pa, 4); + if (validate_attribute (pa, pa_end) == true) + pa = NULL; + while (pa) { if (*pa != attr) @@ -572,6 +723,8 @@ read_attr (struct grub_ntfs_attr *at, grub_uint8_t *dest, grub_disk_addr_t ofs, else vcn = ofs >> (at->mft->data->log_spc + GRUB_NTFS_BLK_SHR); pa = at->attr_nxt + u16at (at->attr_nxt, 4); + if (validate_attribute (pa, at->attr_end) == false) + pa = NULL; while (pa) { diff --git a/include/grub/ntfs.h b/include/grub/ntfs.h index 2c8078403..77b182acf 100644 --- a/include/grub/ntfs.h +++ b/include/grub/ntfs.h @@ -91,6 +91,28 @@ enum #define GRUB_NTFS_ATTRIBUTE_HEADER_SIZE 16 +/* + * To make attribute validation clearer the offsets for each value in the + * attribute headers are defined as macros. + * + * These offsets are all from: + * https://flatcap.github.io/linux-ntfs/ntfs/concepts/attribute_header.html + */ + +/* These offsets are part of the attribute header. */ +#define GRUB_NTFS_ATTRIBUTE_LENGTH 4 +#define GRUB_NTFS_ATTRIBUTE_RESIDENT 8 +#define GRUB_NTFS_ATTRIBUTE_NAME_LENGTH 9 +#define GRUB_NTFS_ATTRIBUTE_NAME_OFFSET 10 + +/* Offsets for values needed for resident data. */ +#define GRUB_NTFS_ATTRIBUTE_RES_LENGTH 16 +#define GRUB_NTFS_ATTRIBUTE_RES_OFFSET 20 + +/* Offsets for values needed for non-resident data. */ +#define GRUB_NTFS_ATTRIBUTE_DATA_RUNS 32 +#define GRUB_NTFS_ATTRIBUTE_COMPRESSION_UNIT_SIZE 34 + enum { GRUB_NTFS_AF_ALST = 1, From 6ccc77b59d16578b10eaf8a4fe85c20b229f0d8a Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Fri, 31 May 2024 15:14:57 +0800 Subject: [PATCH 268/402] fs/xfs: Fix out-of-bounds read The number of records in the root key array read from disk was not being validated against the size of the root node. This could lead to an out-of-bounds read. This patch adds a check to ensure that the number of records in the root key array does not exceed the expected size of a root node read from disk. If this check detects an out-of-bounds condition the operation is aborted to prevent random errors due to metadata corruption. Reported-by: Daniel Axtens Signed-off-by: Michael Chang Reviewed-by: Daniel Kiper --- grub-core/fs/xfs.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index 8e02ab4a3..82ea33f40 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -595,6 +595,17 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) do { grub_uint64_t i; + grub_addr_t keys_end, data_end; + + if (grub_mul (sizeof (grub_uint64_t), nrec, &keys_end) || + grub_add ((grub_addr_t) keys, keys_end, &keys_end) || + grub_add ((grub_addr_t) node->data, node->data->data_size, &data_end) || + keys_end > data_end) + { + grub_error (GRUB_ERR_BAD_FS, "invalid number of XFS root keys"); + grub_free (leaf); + return 0; + } for (i = 0; i < nrec; i++) { From d1d6b7ea58aa5a80a4c4d0666b49460056c8ef0a Mon Sep 17 00:00:00 2001 From: B Horn Date: Sun, 12 May 2024 06:03:58 +0100 Subject: [PATCH 269/402] fs/xfs: Ensuring failing to mount sets a grub_errno It was previously possible for grub_xfs_mount() to return NULL without setting grub_errno if the XFS version was invalid. This resulted in it being possible for grub_dl_unref() to be called twice allowing the XFS module to be unloaded while there were still references to it. Fixing this problem in general by ensuring a grub_errno is set if the fail label is reached. Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/fs/xfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index 82ea33f40..8c0d60f7d 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -327,6 +327,8 @@ static int grub_xfs_sb_valid(struct grub_xfs_data *data) } return 1; } + + grub_error (GRUB_ERR_BAD_FS, "unsupported XFS filesystem version"); return 0; } @@ -1068,7 +1070,7 @@ grub_xfs_mount (grub_disk_t disk) return data; fail: - if (grub_errno == GRUB_ERR_OUT_OF_RANGE) + if (grub_errno == GRUB_ERR_OUT_OF_RANGE || grub_errno == GRUB_ERR_NONE) grub_error (GRUB_ERR_BAD_FS, "not an XFS filesystem"); grub_free (data); From a7910687294b29288ac649e71b47493c93294f17 Mon Sep 17 00:00:00 2001 From: B Horn Date: Sun, 12 May 2024 03:01:40 +0100 Subject: [PATCH 270/402] kern/file: Ensure file->data is set This is to avoid a generic issue were some filesystems would not set data and also not set a grub_errno. This meant it was possible for many filesystems to grub_dl_unref() themselves multiple times resulting in it being possible to unload the filesystems while there were still references to them, e.g., via a loopback. Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/kern/file.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c index 750177248..e990507fc 100644 --- a/grub-core/kern/file.c +++ b/grub-core/kern/file.c @@ -114,6 +114,9 @@ grub_file_open (const char *name, enum grub_file_type type) if ((file->fs->fs_open) (file, file_name) != GRUB_ERR_NONE) goto fail; + if (file->data == NULL) + goto fail; + file->name = grub_strdup (name); grub_errno = GRUB_ERR_NONE; From 16f196874fbe360a1b3c66064ec15adadf94c57b Mon Sep 17 00:00:00 2001 From: B Horn Date: Sun, 12 May 2024 10:15:03 +0100 Subject: [PATCH 271/402] kern/file: Implement filesystem reference counting The grub_file_open() and grub_file_close() should be the only places that allow a reference to a filesystem to stay open. So, add grub_dl_t to grub_fs_t and set this in the GRUB_MOD_INIT() for each filesystem to avoid issues when filesystems forget to do it themselves or do not track their own references, e.g. squash4. The fs_label(), fs_uuid(), fs_mtime() and fs_read() should all ref and unref in the same function but it is essentially redundant in GRUB single threaded model. Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/fs/affs.c | 1 + grub-core/fs/bfs.c | 1 + grub-core/fs/btrfs.c | 1 + grub-core/fs/cbfs.c | 1 + grub-core/fs/cpio.c | 1 + grub-core/fs/cpio_be.c | 1 + grub-core/fs/erofs.c | 1 + grub-core/fs/ext2.c | 1 + grub-core/fs/f2fs.c | 1 + grub-core/fs/fat.c | 1 + grub-core/fs/hfs.c | 1 + grub-core/fs/hfsplus.c | 1 + grub-core/fs/iso9660.c | 1 + grub-core/fs/jfs.c | 1 + grub-core/fs/minix.c | 1 + grub-core/fs/newc.c | 1 + grub-core/fs/nilfs2.c | 1 + grub-core/fs/ntfs.c | 1 + grub-core/fs/odc.c | 1 + grub-core/fs/proc.c | 1 + grub-core/fs/reiserfs.c | 1 + grub-core/fs/romfs.c | 1 + grub-core/fs/sfs.c | 1 + grub-core/fs/squash4.c | 1 + grub-core/fs/tar.c | 1 + grub-core/fs/udf.c | 1 + grub-core/fs/ufs.c | 1 + grub-core/fs/xfs.c | 1 + grub-core/fs/zfs/zfs.c | 1 + grub-core/kern/file.c | 7 +++++++ include/grub/fs.h | 4 ++++ 31 files changed, 40 insertions(+) diff --git a/grub-core/fs/affs.c b/grub-core/fs/affs.c index ed606b3f1..9b0afb954 100644 --- a/grub-core/fs/affs.c +++ b/grub-core/fs/affs.c @@ -703,6 +703,7 @@ static struct grub_fs grub_affs_fs = GRUB_MOD_INIT(affs) { + grub_affs_fs.mod = mod; grub_fs_register (&grub_affs_fs); my_mod = mod; } diff --git a/grub-core/fs/bfs.c b/grub-core/fs/bfs.c index 9bc478ce8..022f69fe2 100644 --- a/grub-core/fs/bfs.c +++ b/grub-core/fs/bfs.c @@ -1106,6 +1106,7 @@ GRUB_MOD_INIT (bfs) { COMPILE_TIME_ASSERT (1 << LOG_EXTENT_SIZE == sizeof (struct grub_bfs_extent)); + grub_bfs_fs.mod = mod; grub_fs_register (&grub_bfs_fs); } diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index ba0c58352..aae81482b 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -2413,6 +2413,7 @@ static struct grub_fs grub_btrfs_fs = { GRUB_MOD_INIT (btrfs) { + grub_btrfs_fs.mod = mod; grub_fs_register (&grub_btrfs_fs); } diff --git a/grub-core/fs/cbfs.c b/grub-core/fs/cbfs.c index 8ab7106af..2332745fe 100644 --- a/grub-core/fs/cbfs.c +++ b/grub-core/fs/cbfs.c @@ -390,6 +390,7 @@ GRUB_MOD_INIT (cbfs) #if (defined (__i386__) || defined (__x86_64__)) && !defined (GRUB_UTIL) && !defined (GRUB_MACHINE_EMU) && !defined (GRUB_MACHINE_XEN) init_cbfsdisk (); #endif + grub_cbfs_fs.mod = mod; grub_fs_register (&grub_cbfs_fs); } diff --git a/grub-core/fs/cpio.c b/grub-core/fs/cpio.c index dab5f9898..1799f7ff5 100644 --- a/grub-core/fs/cpio.c +++ b/grub-core/fs/cpio.c @@ -52,6 +52,7 @@ read_number (const grub_uint16_t *arr, grub_size_t size) GRUB_MOD_INIT (cpio) { + grub_cpio_fs.mod = mod; grub_fs_register (&grub_cpio_fs); } diff --git a/grub-core/fs/cpio_be.c b/grub-core/fs/cpio_be.c index 846548892..7bed1b848 100644 --- a/grub-core/fs/cpio_be.c +++ b/grub-core/fs/cpio_be.c @@ -52,6 +52,7 @@ read_number (const grub_uint16_t *arr, grub_size_t size) GRUB_MOD_INIT (cpio_be) { + grub_cpio_fs.mod = mod; grub_fs_register (&grub_cpio_fs); } diff --git a/grub-core/fs/erofs.c b/grub-core/fs/erofs.c index f2a82e988..ae38b045e 100644 --- a/grub-core/fs/erofs.c +++ b/grub-core/fs/erofs.c @@ -991,6 +991,7 @@ static struct grub_fs grub_erofs_fs = { GRUB_MOD_INIT (erofs) { + grub_erofs_fs.mod = mod; grub_fs_register (&grub_erofs_fs); } diff --git a/grub-core/fs/ext2.c b/grub-core/fs/ext2.c index 3f9f6b208..c3058f7e7 100644 --- a/grub-core/fs/ext2.c +++ b/grub-core/fs/ext2.c @@ -1131,6 +1131,7 @@ static struct grub_fs grub_ext2_fs = GRUB_MOD_INIT(ext2) { + grub_ext2_fs.mod = mod; grub_fs_register (&grub_ext2_fs); my_mod = mod; } diff --git a/grub-core/fs/f2fs.c b/grub-core/fs/f2fs.c index db8a65f8d..f6d6beaa5 100644 --- a/grub-core/fs/f2fs.c +++ b/grub-core/fs/f2fs.c @@ -1353,6 +1353,7 @@ static struct grub_fs grub_f2fs_fs = { GRUB_MOD_INIT (f2fs) { + grub_f2fs_fs.mod = mod; grub_fs_register (&grub_f2fs_fs); my_mod = mod; } diff --git a/grub-core/fs/fat.c b/grub-core/fs/fat.c index c5efed724..6e62b915d 100644 --- a/grub-core/fs/fat.c +++ b/grub-core/fs/fat.c @@ -1312,6 +1312,7 @@ GRUB_MOD_INIT(fat) #endif { COMPILE_TIME_ASSERT (sizeof (struct grub_fat_dir_entry) == 32); + grub_fat_fs.mod = mod; grub_fs_register (&grub_fat_fs); my_mod = mod; } diff --git a/grub-core/fs/hfs.c b/grub-core/fs/hfs.c index 920112b03..ce7581dd5 100644 --- a/grub-core/fs/hfs.c +++ b/grub-core/fs/hfs.c @@ -1434,6 +1434,7 @@ static struct grub_fs grub_hfs_fs = GRUB_MOD_INIT(hfs) { + grub_hfs_fs.mod = mod; if (!grub_is_lockdown ()) grub_fs_register (&grub_hfs_fs); my_mod = mod; diff --git a/grub-core/fs/hfsplus.c b/grub-core/fs/hfsplus.c index de71fd486..3f203abcc 100644 --- a/grub-core/fs/hfsplus.c +++ b/grub-core/fs/hfsplus.c @@ -1176,6 +1176,7 @@ static struct grub_fs grub_hfsplus_fs = GRUB_MOD_INIT(hfsplus) { + grub_hfsplus_fs.mod = mod; grub_fs_register (&grub_hfsplus_fs); my_mod = mod; } diff --git a/grub-core/fs/iso9660.c b/grub-core/fs/iso9660.c index 8e3c95c4f..c73cb9ce0 100644 --- a/grub-core/fs/iso9660.c +++ b/grub-core/fs/iso9660.c @@ -1260,6 +1260,7 @@ static struct grub_fs grub_iso9660_fs = GRUB_MOD_INIT(iso9660) { + grub_iso9660_fs.mod = mod; grub_fs_register (&grub_iso9660_fs); my_mod = mod; } diff --git a/grub-core/fs/jfs.c b/grub-core/fs/jfs.c index c06d174c9..a82800ac3 100644 --- a/grub-core/fs/jfs.c +++ b/grub-core/fs/jfs.c @@ -1004,6 +1004,7 @@ static struct grub_fs grub_jfs_fs = GRUB_MOD_INIT(jfs) { + grub_jfs_fs.mod = mod; grub_fs_register (&grub_jfs_fs); my_mod = mod; } diff --git a/grub-core/fs/minix.c b/grub-core/fs/minix.c index 5354951d1..b7679c3e2 100644 --- a/grub-core/fs/minix.c +++ b/grub-core/fs/minix.c @@ -734,6 +734,7 @@ GRUB_MOD_INIT(minix) #endif #endif { + grub_minix_fs.mod = mod; grub_fs_register (&grub_minix_fs); my_mod = mod; } diff --git a/grub-core/fs/newc.c b/grub-core/fs/newc.c index 4fb8b2e3d..43b7f8b64 100644 --- a/grub-core/fs/newc.c +++ b/grub-core/fs/newc.c @@ -64,6 +64,7 @@ read_number (const char *str, grub_size_t size) GRUB_MOD_INIT (newc) { + grub_cpio_fs.mod = mod; grub_fs_register (&grub_cpio_fs); } diff --git a/grub-core/fs/nilfs2.c b/grub-core/fs/nilfs2.c index fc7374ead..4e1e71738 100644 --- a/grub-core/fs/nilfs2.c +++ b/grub-core/fs/nilfs2.c @@ -1231,6 +1231,7 @@ GRUB_MOD_INIT (nilfs2) grub_nilfs2_dat_entry)); COMPILE_TIME_ASSERT (1 << LOG_INODE_SIZE == sizeof (struct grub_nilfs2_inode)); + grub_nilfs2_fs.mod = mod; grub_fs_register (&grub_nilfs2_fs); my_mod = mod; } diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index 64f4f2221..4e144cc3c 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -1541,6 +1541,7 @@ static struct grub_fs grub_ntfs_fs = GRUB_MOD_INIT (ntfs) { + grub_ntfs_fs.mod = mod; grub_fs_register (&grub_ntfs_fs); my_mod = mod; } diff --git a/grub-core/fs/odc.c b/grub-core/fs/odc.c index 790000622..8e4e8aeac 100644 --- a/grub-core/fs/odc.c +++ b/grub-core/fs/odc.c @@ -52,6 +52,7 @@ read_number (const char *str, grub_size_t size) GRUB_MOD_INIT (odc) { + grub_cpio_fs.mod = mod; grub_fs_register (&grub_cpio_fs); } diff --git a/grub-core/fs/proc.c b/grub-core/fs/proc.c index 5f516502d..bcde43349 100644 --- a/grub-core/fs/proc.c +++ b/grub-core/fs/proc.c @@ -192,6 +192,7 @@ static struct grub_fs grub_procfs_fs = GRUB_MOD_INIT (procfs) { + grub_procfs_fs.mod = mod; grub_disk_dev_register (&grub_procfs_dev); grub_fs_register (&grub_procfs_fs); } diff --git a/grub-core/fs/reiserfs.c b/grub-core/fs/reiserfs.c index 36b26ac98..c3850e013 100644 --- a/grub-core/fs/reiserfs.c +++ b/grub-core/fs/reiserfs.c @@ -1417,6 +1417,7 @@ static struct grub_fs grub_reiserfs_fs = GRUB_MOD_INIT(reiserfs) { + grub_reiserfs_fs.mod = mod; grub_fs_register (&grub_reiserfs_fs); my_mod = mod; } diff --git a/grub-core/fs/romfs.c b/grub-core/fs/romfs.c index 1f7dcfca1..56b0b2b2f 100644 --- a/grub-core/fs/romfs.c +++ b/grub-core/fs/romfs.c @@ -475,6 +475,7 @@ static struct grub_fs grub_romfs_fs = GRUB_MOD_INIT(romfs) { + grub_romfs_fs.mod = mod; grub_fs_register (&grub_romfs_fs); } diff --git a/grub-core/fs/sfs.c b/grub-core/fs/sfs.c index 983e88008..f0d7cac43 100644 --- a/grub-core/fs/sfs.c +++ b/grub-core/fs/sfs.c @@ -779,6 +779,7 @@ static struct grub_fs grub_sfs_fs = GRUB_MOD_INIT(sfs) { + grub_sfs_fs.mod = mod; grub_fs_register (&grub_sfs_fs); my_mod = mod; } diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index a30e6ebe1..6e9d63874 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -1044,6 +1044,7 @@ static struct grub_fs grub_squash_fs = GRUB_MOD_INIT(squash4) { + grub_squash_fs.mod = mod; grub_fs_register (&grub_squash_fs); } diff --git a/grub-core/fs/tar.c b/grub-core/fs/tar.c index 386c09022..fd2ec1f74 100644 --- a/grub-core/fs/tar.c +++ b/grub-core/fs/tar.c @@ -354,6 +354,7 @@ static struct grub_fs grub_cpio_fs = { GRUB_MOD_INIT (tar) { + grub_cpio_fs.mod = mod; grub_fs_register (&grub_cpio_fs); } diff --git a/grub-core/fs/udf.c b/grub-core/fs/udf.c index b836e6107..8765c633c 100644 --- a/grub-core/fs/udf.c +++ b/grub-core/fs/udf.c @@ -1455,6 +1455,7 @@ static struct grub_fs grub_udf_fs = { GRUB_MOD_INIT (udf) { + grub_udf_fs.mod = mod; grub_fs_register (&grub_udf_fs); my_mod = mod; } diff --git a/grub-core/fs/ufs.c b/grub-core/fs/ufs.c index 01235101b..e82d9356d 100644 --- a/grub-core/fs/ufs.c +++ b/grub-core/fs/ufs.c @@ -899,6 +899,7 @@ GRUB_MOD_INIT(ufs1) #endif #endif { + grub_ufs_fs.mod = mod; grub_fs_register (&grub_ufs_fs); my_mod = mod; } diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index 8c0d60f7d..732c4aaf3 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -1304,6 +1304,7 @@ static struct grub_fs grub_xfs_fs = GRUB_MOD_INIT(xfs) { + grub_xfs_fs.mod = mod; grub_fs_register (&grub_xfs_fs); my_mod = mod; } diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index 3fdf9bda8..22ced4014 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -4464,6 +4464,7 @@ static struct grub_fs grub_zfs_fs = { GRUB_MOD_INIT (zfs) { COMPILE_TIME_ASSERT (sizeof (zap_leaf_chunk_t) == ZAP_LEAF_CHUNKSIZE); + grub_zfs_fs.mod = mod; grub_fs_register (&grub_zfs_fs); #ifndef GRUB_UTIL my_mod = mod; diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c index e990507fc..6e7efe89a 100644 --- a/grub-core/kern/file.c +++ b/grub-core/kern/file.c @@ -25,6 +25,7 @@ #include #include #include +#include void (*EXPORT_VAR (grub_grubnet_fini)) (void); @@ -117,6 +118,9 @@ grub_file_open (const char *name, enum grub_file_type type) if (file->data == NULL) goto fail; + if (file->fs->mod) + grub_dl_ref (file->fs->mod); + file->name = grub_strdup (name); grub_errno = GRUB_ERR_NONE; @@ -197,6 +201,9 @@ 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); diff --git a/include/grub/fs.h b/include/grub/fs.h index 026bc3bb8..df4c93b16 100644 --- a/include/grub/fs.h +++ b/include/grub/fs.h @@ -23,6 +23,7 @@ #include #include #include +#include #include /* For embedding types. */ @@ -57,6 +58,9 @@ struct grub_fs /* My name. */ const char *name; + /* My module */ + grub_dl_t mod; + /* Call HOOK with each file under DIR. */ grub_err_t (*fs_dir) (grub_device_t device, const char *path, grub_fs_dir_hook_t hook, void *hook_data); From 13febd78db3cd85dcba67d8ad03ad4d42815f11e Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Thu, 29 Aug 2024 13:27:30 +0800 Subject: [PATCH 272/402] disk/cryptodisk: Require authentication after TPM unlock for CLI access The GRUB may use TPM to verify the integrity of boot components and the result can determine whether a previously sealed key can be released. If everything checks out, showing nothing has been tampered with, the key is released and GRUB unlocks the encrypted root partition for the next stage of booting. However, the liberal Command Line Interface (CLI) can be misused by anyone in this case to access files in the encrypted partition one way or another. Despite efforts to keep the CLI secure by preventing utility command output from leaking file content, many techniques in the wild could still be used to exploit the CLI, enabling attacks or learning methods to attack. It's nearly impossible to account for all scenarios where a hack could be applied. Therefore, to mitigate potential misuse of the CLI after the root device has been successfully unlocked via TPM, the user should be required to authenticate using the LUKS password. This added layer of security ensures that only authorized users can access the CLI reducing the risk of exploitation or unauthorized access to the encrypted partition. Fixes: CVE-2024-49504 Signed-off-by: Michael Chang Reviewed-by: Daniel Kiper --- docs/grub.texi | 30 ++++++++++++ grub-core/disk/cryptodisk.c | 86 +++++++++++++++++++++++++++++++++++ grub-core/kern/main.c | 12 +++++ grub-core/normal/auth.c | 30 ++++++++++++ grub-core/normal/main.c | 4 ++ grub-core/normal/menu_entry.c | 4 ++ include/grub/auth.h | 1 + include/grub/cryptodisk.h | 3 ++ include/grub/misc.h | 2 + 9 files changed, 172 insertions(+) diff --git a/docs/grub.texi b/docs/grub.texi index 200e747af..e914e022b 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -9119,6 +9119,36 @@ command through the swtpm control channel. # @kbd{swtpm_ioctl -s --unix swtpm-state/ctrl} @end example +@subsection Command line and menuentry editor protection + +The TPM key protector provides full disk encryption support on servers or +virtual machine images, meanwhile keeping the boot process unattended. This +prevents service disruptions by eliminating the need for manual password input +during startup, improving system uptime and continuity. It is achieved by TPM, +which verifies the integrity of boot components by checking cryptographic +hashes against securely stored values, to confirm the disks are unlocked in a +trusted state. + +However, for users to access the system interactively, some form of +authentication is still required, as the disks are not unlocked by an +authorized user. This raised concerns about using an unprotected +@samp{command-line interface} (@pxref{Command-line interface}), as anyone could +execute commands to access decrypted data. To address this issue, the LUKS +password is used to ensure that only authorized users are granted access to the +interface. Additionally, the @samp{menu entry editor} (@pxref{Menu entry +editor}) is also safeguarded by the LUKS password, as modifying a boot entry is +effectively the same as altering the @file{grub.cfg} file read from encrypted +files. + +It is worth mentioning that the built-in password support, as described in +@samp{Authentication and Authorization in GRUB} (@pxref{Authentication and +authorisation}), can also be used to protect the command-line interface from +unauthorized access. However, it is not recommended to rely on this approach as +it is an optional step. Setting it up requires additional manual intervention, +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 Platform limitations @chapter Platform limitations diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index 5fc41979e..45adffdd9 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -1186,6 +1186,9 @@ grub_cryptodisk_scan_device_real (const char *name, ret = grub_cryptodisk_insert (dev, name, source); if (ret != GRUB_ERR_NONE) goto error; +#ifndef GRUB_UTIL + grub_cli_set_auth_needed (); +#endif goto cleanup; } } @@ -1754,6 +1757,89 @@ luks_script_get (grub_size_t *sz) return ret; } +#ifdef GRUB_MACHINE_EFI +grub_err_t +grub_cryptodisk_challenge_password (void) +{ + grub_cryptodisk_t cr_dev; + + for (cr_dev = cryptodisk_list; cr_dev != NULL; cr_dev = cr_dev->next) + { + grub_cryptodisk_dev_t cr; + grub_disk_t source = NULL; + grub_err_t ret = GRUB_ERR_NONE; + grub_cryptodisk_t dev = NULL; + char *part = NULL; + struct grub_cryptomount_args cargs = {0}; + + cargs.check_boot = 0; + cargs.search_uuid = cr_dev->uuid; + + source = grub_disk_open (cr_dev->source); + + if (source == NULL) + { + ret = grub_errno; + goto error_out; + } + + FOR_CRYPTODISK_DEVS (cr) + { + dev = cr->scan (source, &cargs); + if (grub_errno) + { + ret = grub_errno; + goto error_out; + } + if (dev == NULL) + continue; + break; + } + + if (dev == NULL) + { + ret = grub_error (GRUB_ERR_BAD_MODULE, "no cryptodisk module can handle this device"); + goto error_out; + } + + part = grub_partition_get_name (source->partition); + grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "), source->name, + source->partition != NULL ? "," : "", + part != NULL ? part : N_("UNKNOWN"), cr_dev->uuid); + grub_free (part); + + cargs.key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE); + if (cargs.key_data == NULL) + { + ret = grub_errno; + goto error_out; + } + + if (!grub_password_get ((char *) cargs.key_data, GRUB_CRYPTODISK_MAX_PASSPHRASE)) + { + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "passphrase not supplied"); + goto error_out; + } + cargs.key_len = grub_strlen ((char *) cargs.key_data); + ret = cr->recover_key (source, dev, &cargs); + + error_out: + grub_disk_close (source); + if (dev != NULL) + cryptodisk_close (dev); + if (cargs.key_data) + { + grub_memset (cargs.key_data, 0, cargs.key_len); + grub_free (cargs.key_data); + } + + return ret; + } + + return GRUB_ERR_NONE; +} +#endif /* GRUB_MACHINE_EFI */ + struct grub_procfs_entry luks_script = { .name = "luks_script", diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c index d29494d54..143a232b8 100644 --- a/grub-core/kern/main.c +++ b/grub-core/kern/main.c @@ -38,6 +38,7 @@ #endif static bool cli_disabled = false; +static bool cli_need_auth = false; grub_addr_t grub_modules_get_end (void) @@ -247,6 +248,17 @@ grub_is_cli_disabled (void) return cli_disabled; } +bool +grub_is_cli_need_auth (void) +{ + return cli_need_auth; +} + +void grub_cli_set_auth_needed (void) +{ + cli_need_auth = true; +} + static void check_is_cli_disabled (void) { diff --git a/grub-core/normal/auth.c b/grub-core/normal/auth.c index d94020186..71b361bc0 100644 --- a/grub-core/normal/auth.c +++ b/grub-core/normal/auth.c @@ -25,6 +25,10 @@ #include #include +#ifdef GRUB_MACHINE_EFI +#include +#endif + struct grub_auth_user { struct grub_auth_user *next; @@ -200,6 +204,32 @@ grub_username_get (char buf[], unsigned buf_size) return (key != GRUB_TERM_ESC); } +grub_err_t +grub_auth_check_cli_access (void) +{ + if (grub_is_cli_need_auth () == true) + { +#ifdef GRUB_MACHINE_EFI + static bool authenticated = false; + + if (authenticated == false) + { + grub_err_t ret; + + ret = grub_cryptodisk_challenge_password (); + if (ret == GRUB_ERR_NONE) + authenticated = true; + return ret; + } + return GRUB_ERR_NONE; +#else + return GRUB_ACCESS_DENIED; +#endif + } + + return GRUB_ERR_NONE; +} + grub_err_t grub_auth_check_authentication (const char *userlist) { diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index bd4431000..90879dc21 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -453,9 +453,13 @@ grub_cmdline_run (int nested, int force_auth) } while (err && force_auth); + if (err == GRUB_ERR_NONE) + err = grub_auth_check_cli_access (); + if (err) { grub_print_error (); + grub_wait_after_message (); grub_errno = GRUB_ERR_NONE; return; } diff --git a/grub-core/normal/menu_entry.c b/grub-core/normal/menu_entry.c index ade56be2b..8b0d17e3f 100644 --- a/grub-core/normal/menu_entry.c +++ b/grub-core/normal/menu_entry.c @@ -1255,9 +1255,13 @@ grub_menu_entry_run (grub_menu_entry_t entry) err = grub_auth_check_authentication (NULL); + if (err == GRUB_ERR_NONE) + err = grub_auth_check_cli_access (); + if (err) { grub_print_error (); + grub_wait_after_message (); grub_errno = GRUB_ERR_NONE; return; } diff --git a/include/grub/auth.h b/include/grub/auth.h index 747334451..21d5190f0 100644 --- a/include/grub/auth.h +++ b/include/grub/auth.h @@ -33,5 +33,6 @@ grub_err_t grub_auth_unregister_authentication (const char *user); grub_err_t grub_auth_authenticate (const char *user); grub_err_t grub_auth_deauthenticate (const char *user); grub_err_t grub_auth_check_authentication (const char *userlist); +grub_err_t grub_auth_check_cli_access (void); #endif /* ! GRUB_AUTH_HEADER */ diff --git a/include/grub/cryptodisk.h b/include/grub/cryptodisk.h index 59b461e7a..5bb15751d 100644 --- a/include/grub/cryptodisk.h +++ b/include/grub/cryptodisk.h @@ -203,4 +203,7 @@ grub_util_get_geli_uuid (const char *dev); grub_cryptodisk_t grub_cryptodisk_get_by_uuid (const char *uuid); grub_cryptodisk_t grub_cryptodisk_get_by_source_disk (grub_disk_t disk); +#ifdef GRUB_MACHINE_EFI +grub_err_t grub_cryptodisk_challenge_password (void); +#endif #endif diff --git a/include/grub/misc.h b/include/grub/misc.h index 14d8f37ac..e087e7b3e 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -431,6 +431,8 @@ grub_uint64_t EXPORT_FUNC(grub_divmod64) (grub_uint64_t n, grub_uint64_t *r); extern bool EXPORT_FUNC(grub_is_cli_disabled) (void); +extern bool EXPORT_FUNC(grub_is_cli_need_auth) (void); +extern void EXPORT_FUNC(grub_cli_set_auth_needed) (void); /* Must match softdiv group in gentpl.py. */ #if !defined(GRUB_MACHINE_EMU) && (defined(__arm__) || defined(__ia64__) || \ From 67f70f70a36b6e87a65f928fe1e840a12eafb7ae Mon Sep 17 00:00:00 2001 From: B Horn Date: Sun, 12 May 2024 03:26:19 +0100 Subject: [PATCH 273/402] disk/loopback: Reference tracking for the loopback It was possible to delete a loopback while there were still references to it. This led to an exploitable use-after-free. Fixed by implementing a reference counting in the grub_loopback struct. Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/disk/loopback.c | 18 ++++++++++++++++++ include/grub/err.h | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/grub-core/disk/loopback.c b/grub-core/disk/loopback.c index 4635dcfde..2bea4e922 100644 --- a/grub-core/disk/loopback.c +++ b/grub-core/disk/loopback.c @@ -24,6 +24,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -33,6 +34,7 @@ struct grub_loopback grub_file_t file; struct grub_loopback *next; unsigned long id; + grub_uint64_t refcnt; }; static struct grub_loopback *loopback_list; @@ -64,6 +66,8 @@ delete_loopback (const char *name) if (! dev) return grub_error (GRUB_ERR_BAD_DEVICE, "device not found"); + if (dev->refcnt > 0) + return grub_error (GRUB_ERR_STILL_REFERENCED, "device still referenced"); /* Remove the device from the list. */ *prev = dev->next; @@ -120,6 +124,7 @@ grub_cmd_loopback (grub_extcmd_context_t ctxt, int argc, char **args) newdev->file = file; newdev->id = last_id++; + newdev->refcnt = 0; /* Add the new entry to the list. */ newdev->next = loopback_list; @@ -161,6 +166,9 @@ grub_loopback_open (const char *name, grub_disk_t disk) if (! dev) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); + if (grub_add (dev->refcnt, 1, &dev->refcnt)) + grub_fatal ("Reference count overflow"); + /* Use the filesize for the disk size, round up to a complete sector. */ if (dev->file->size != GRUB_FILE_SIZE_UNKNOWN) disk->total_sectors = ((dev->file->size + GRUB_DISK_SECTOR_SIZE - 1) @@ -178,6 +186,15 @@ grub_loopback_open (const char *name, grub_disk_t disk) return 0; } +static void +grub_loopback_close (grub_disk_t disk) +{ + struct grub_loopback *dev = disk->data; + + if (grub_sub (dev->refcnt, 1, &dev->refcnt)) + grub_fatal ("Reference count underflow"); +} + static grub_err_t grub_loopback_read (grub_disk_t disk, grub_disk_addr_t sector, grub_size_t size, char *buf) @@ -220,6 +237,7 @@ static struct grub_disk_dev grub_loopback_dev = .id = GRUB_DISK_DEVICE_LOOPBACK_ID, .disk_iterate = grub_loopback_iterate, .disk_open = grub_loopback_open, + .disk_close = grub_loopback_close, .disk_read = grub_loopback_read, .disk_write = grub_loopback_write, .next = 0 diff --git a/include/grub/err.h b/include/grub/err.h index 1c07034cd..b0e54e0a0 100644 --- a/include/grub/err.h +++ b/include/grub/err.h @@ -73,7 +73,8 @@ typedef enum GRUB_ERR_NET_NO_DOMAIN, GRUB_ERR_EOF, GRUB_ERR_BAD_SIGNATURE, - GRUB_ERR_BAD_FIRMWARE + GRUB_ERR_BAD_FIRMWARE, + GRUB_ERR_STILL_REFERENCED } grub_err_t; From 18212f0648b6de7d71d4c8f41eb4d8b78b3a299b Mon Sep 17 00:00:00 2001 From: B Horn Date: Sun, 12 May 2024 04:09:24 +0100 Subject: [PATCH 274/402] kern/disk: Limit recursion depth The grub_disk_read() may trigger other disk reads, e.g. via loopbacks. This may lead to very deep recursion which can corrupt the heap. So, fix the issue by limiting reads depth. Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/kern/disk.c | 27 ++++++++++++++++++++------- include/grub/err.h | 3 ++- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/grub-core/kern/disk.c b/grub-core/kern/disk.c index 1eda58fe9..82e04fd00 100644 --- a/grub-core/kern/disk.c +++ b/grub-core/kern/disk.c @@ -28,6 +28,10 @@ #define GRUB_CACHE_TIMEOUT 2 +/* Disk reads may trigger other disk reads. So, limit recursion depth. */ +#define MAX_READ_RECURSION_DEPTH 16 +static unsigned int read_recursion_depth = 0; + /* The last time the disk was used. */ static grub_uint64_t grub_last_time = 0; @@ -417,6 +421,8 @@ grub_err_t grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, grub_off_t offset, grub_size_t size, void *buf) { + grub_err_t err = GRUB_ERR_NONE; + /* First of all, check if the region is within the disk. */ if (grub_disk_adjust_range (disk, §or, &offset, size) != GRUB_ERR_NONE) { @@ -427,12 +433,17 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, return grub_errno; } + if (++read_recursion_depth >= MAX_READ_RECURSION_DEPTH) + { + grub_error (GRUB_ERR_RECURSION_DEPTH, "grub_disk_read(): Maximum recursion depth exceeded"); + goto error; + } + /* First read until first cache boundary. */ if (offset || (sector & (GRUB_DISK_CACHE_SIZE - 1))) { grub_disk_addr_t start_sector; grub_size_t pos; - grub_err_t err; grub_size_t len; start_sector = sector & ~((grub_disk_addr_t) GRUB_DISK_CACHE_SIZE - 1); @@ -444,7 +455,7 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, err = grub_disk_read_small (disk, start_sector, offset + pos, len, buf); if (err) - return err; + goto error; buf = (char *) buf + len; size -= len; offset += len; @@ -457,7 +468,6 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, { char *data = NULL; grub_disk_addr_t agglomerate; - grub_err_t err; /* agglomerate read until we find a first cached entry. */ for (agglomerate = 0; agglomerate @@ -493,7 +503,7 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, - disk->log_sector_size), buf); if (err) - return err; + goto error; for (i = 0; i < agglomerate; i ++) grub_disk_cache_store (disk->dev->id, disk->id, @@ -527,13 +537,16 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, /* And now read the last part. */ if (size) { - grub_err_t err; err = grub_disk_read_small (disk, sector, 0, size, buf); if (err) - return err; + goto error; } - return grub_errno; + err = grub_errno; + + error: + read_recursion_depth--; + return err; } grub_uint64_t diff --git a/include/grub/err.h b/include/grub/err.h index b0e54e0a0..202fa8a7a 100644 --- a/include/grub/err.h +++ b/include/grub/err.h @@ -74,7 +74,8 @@ typedef enum GRUB_ERR_EOF, GRUB_ERR_BAD_SIGNATURE, GRUB_ERR_BAD_FIRMWARE, - GRUB_ERR_STILL_REFERENCED + GRUB_ERR_STILL_REFERENCED, + GRUB_ERR_RECURSION_DEPTH } grub_err_t; From 8a7103fddfd6664f41081f3bb88eebbf2871da2a Mon Sep 17 00:00:00 2001 From: B Horn Date: Sat, 16 Nov 2024 21:24:19 +0000 Subject: [PATCH 275/402] kern/partition: Limit recursion in part_iterate() The part_iterate() is used by grub_partition_iterate() as a callback in the partition iterate functions. However, part_iterate() may also call the partition iterate functions which may lead to recursion. Fix potential issue by limiting the recursion depth. Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/kern/partition.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/grub-core/kern/partition.c b/grub-core/kern/partition.c index edad9f9e4..704512a20 100644 --- a/grub-core/kern/partition.c +++ b/grub-core/kern/partition.c @@ -28,6 +28,9 @@ grub_partition_map_t grub_partition_map_list; +#define MAX_RECURSION_DEPTH 32 +static unsigned int recursion_depth = 0; + /* * Checks that disk->partition contains part. This function assumes that the * start of part is relative to the start of disk->partition. Returns 1 if @@ -208,7 +211,12 @@ part_iterate (grub_disk_t dsk, const grub_partition_t partition, void *data) FOR_PARTITION_MAPS(partmap) { grub_err_t err; - err = partmap->iterate (dsk, part_iterate, ctx); + recursion_depth++; + if (recursion_depth <= MAX_RECURSION_DEPTH) + err = partmap->iterate (dsk, part_iterate, ctx); + else + err = grub_error (GRUB_ERR_RECURSION_DEPTH, "maximum recursion depth exceeded"); + recursion_depth--; if (err) grub_errno = GRUB_ERR_NONE; if (ctx->ret) From d8a937ccae5c6d86dc4375698afca5cefdcd01e1 Mon Sep 17 00:00:00 2001 From: B Horn Date: Thu, 18 Apr 2024 19:04:13 +0100 Subject: [PATCH 276/402] script/execute: Limit the recursion depth If unbounded recursion is allowed it becomes possible to collide the stack with the heap. As UEFI firmware often lacks guard pages this becomes an exploitable issue as it is possible in some cases to do a controlled overwrite of a section of this heap region with arbitrary data. Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/script/execute.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/grub-core/script/execute.c b/grub-core/script/execute.c index 14ff09094..e1450f45d 100644 --- a/grub-core/script/execute.c +++ b/grub-core/script/execute.c @@ -33,10 +33,18 @@ is sizeof (int) * 3, and one extra for a possible -ve sign. */ #define ERRNO_DIGITS_MAX (sizeof (int) * 3 + 1) +/* + * A limit on recursion, to avoid colliding with the heap. UEFI defines a baseline + * stack size of 128 KiB. So, assuming at most 1-2 KiB per iteration this should + * keep us safe. + */ +#define MAX_RECURSION_DEPTH 64 + static unsigned long is_continue; static unsigned long active_loops; static unsigned long active_breaks; static unsigned long function_return; +static unsigned long recursion_depth; #define GRUB_SCRIPT_SCOPE_MALLOCED 1 #define GRUB_SCRIPT_SCOPE_ARGS_MALLOCED 2 @@ -816,7 +824,13 @@ grub_script_execute_cmd (struct grub_script_cmd *cmd) if (cmd == 0) return 0; + recursion_depth++; + + if (recursion_depth >= MAX_RECURSION_DEPTH) + return grub_error (GRUB_ERR_RECURSION_DEPTH, N_("maximum recursion depth exceeded")); + ret = cmd->exec (cmd); + recursion_depth--; grub_snprintf (errnobuf, sizeof (errnobuf), "%d", ret); grub_env_set ("?", errnobuf); From a1dd8e59da26f1a9608381d3a1a6c0f465282b1d Mon Sep 17 00:00:00 2001 From: B Horn Date: Thu, 28 Nov 2024 04:05:04 +0000 Subject: [PATCH 277/402] net: Unregister net_default_ip and net_default_mac variables hooks on unload The net module is a dependency of normal. So, it shouldn't be possible to unload the net. Though unregister variables hooks as a precaution. It also gets in line with unregistering the other net module hooks. Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/net/net.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 8cad4fb6d..f69c67b64 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -2072,6 +2072,8 @@ GRUB_MOD_FINI(net) { grub_register_variable_hook ("net_default_server", 0, 0); grub_register_variable_hook ("pxe_default_server", 0, 0); + grub_register_variable_hook ("net_default_ip", 0, 0); + grub_register_variable_hook ("net_default_mac", 0, 0); grub_bootp_fini (); grub_dns_fini (); From aa8b4d7facef7b75a2703274b1b9d4e0e734c401 Mon Sep 17 00:00:00 2001 From: B Horn Date: Fri, 1 Nov 2024 23:49:48 +0000 Subject: [PATCH 278/402] net: Remove variables hooks when interface is unregisted The grub_net_network_level_interface_unregister(), previously implemented in a header, did not remove the variables hooks that were registered in grub_net_network_level_interface_register(). Fix this by implementing the same logic used to register the variables and move the function into the grub-core/net/net.c. Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/net/net.c | 32 ++++++++++++++++++++++++++++++++ include/grub/net.h | 11 +---------- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/grub-core/net/net.c b/grub-core/net/net.c index f69c67b64..0e41e21a5 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -984,6 +984,38 @@ grub_net_network_level_interface_register (struct grub_net_network_level_interfa grub_net_network_level_interfaces = inter; } +void +grub_net_network_level_interface_unregister (struct grub_net_network_level_interface *inter) +{ + char *name; + + { + char buf[GRUB_NET_MAX_STR_HWADDR_LEN]; + + grub_net_hwaddr_to_str (&inter->hwaddress, buf); + name = grub_xasprintf ("net_%s_mac", inter->name); + if (name != NULL) + grub_register_variable_hook (name, NULL, NULL); + grub_free (name); + } + + { + char buf[GRUB_NET_MAX_STR_ADDR_LEN]; + + grub_net_addr_to_str (&inter->address, buf); + name = grub_xasprintf ("net_%s_ip", inter->name); + if (name != NULL) + grub_register_variable_hook (name, NULL, NULL); + grub_free (name); + } + + inter->card->num_ifaces--; + *inter->prev = inter->next; + if (inter->next) + inter->next->prev = inter->prev; + inter->next = 0; + inter->prev = 0; +} grub_err_t grub_net_add_ipv4_local (struct grub_net_network_level_interface *inter, diff --git a/include/grub/net.h b/include/grub/net.h index 844e501c1..228d04963 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -540,16 +540,7 @@ void grub_bootp_fini (void); void grub_dns_init (void); void grub_dns_fini (void); -static inline void -grub_net_network_level_interface_unregister (struct grub_net_network_level_interface *inter) -{ - inter->card->num_ifaces--; - *inter->prev = inter->next; - if (inter->next) - inter->next->prev = inter->prev; - inter->next = 0; - inter->prev = 0; -} +void grub_net_network_level_interface_unregister (struct grub_net_network_level_interface *inter); void grub_net_tcp_retransmit (void); From 5eef88152833062a3f7e017535372d64ac8ef7e1 Mon Sep 17 00:00:00 2001 From: B Horn Date: Fri, 15 Nov 2024 13:12:09 +0000 Subject: [PATCH 279/402] net: Fix OOB write in grub_net_search_config_file() The function included a call to grub_strcpy() which copied data from an environment variable to a buffer allocated in grub_cmd_normal(). The grub_cmd_normal() didn't consider the length of the environment variable. So, the copy operation could exceed the allocation and lead to an OOB write. Fix the issue by replacing grub_strcpy() with grub_strlcpy() and pass the underlying buffers size to the grub_net_search_config_file(). Fixes: CVE-2025-0624 Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/net/net.c | 7 ++++--- grub-core/normal/main.c | 2 +- include/grub/net.h | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 0e41e21a5..9939ff601 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -1941,14 +1941,15 @@ grub_config_search_through (char *config, char *suffix, } grub_err_t -grub_net_search_config_file (char *config) +grub_net_search_config_file (char *config, grub_size_t config_buf_len) { - grub_size_t config_len; + grub_size_t config_len, suffix_len; char *suffix; config_len = grub_strlen (config); config[config_len] = '-'; suffix = config + config_len + 1; + suffix_len = config_buf_len - (config_len + 1); struct grub_net_network_level_interface *inf; FOR_NET_NETWORK_LEVEL_INTERFACES (inf) @@ -1974,7 +1975,7 @@ grub_net_search_config_file (char *config) if (client_uuid) { - grub_strcpy (suffix, client_uuid); + grub_strlcpy (suffix, client_uuid, suffix_len); if (grub_config_search_through (config, suffix, 1, 0) == 0) return GRUB_ERR_NONE; } diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index 90879dc21..838f57fa5 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -344,7 +344,7 @@ grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)), if (grub_strncmp (prefix + 1, "tftp", sizeof ("tftp") - 1) == 0 && !disable_net_search) - grub_net_search_config_file (config); + grub_net_search_config_file (config, config_len); grub_enter_normal_mode (config); grub_free (config); diff --git a/include/grub/net.h b/include/grub/net.h index 228d04963..58a4f83fc 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -570,7 +570,7 @@ void grub_net_remove_dns_server (const struct grub_net_network_level_address *s); grub_err_t -grub_net_search_config_file (char *config); +grub_net_search_config_file (char *config, grub_size_t config_buf_len); extern char *grub_net_default_server; From 0707accab1b9be5d3645d4700dde3f99209f9367 Mon Sep 17 00:00:00 2001 From: B Horn Date: Thu, 18 Apr 2024 17:32:34 +0100 Subject: [PATCH 280/402] net/tftp: Fix stack buffer overflow in tftp_open() An overly long filename can be passed to tftp_open() which would cause grub_normalize_filename() to write out of bounds. Fixed by adding an extra argument to grub_normalize_filename() for the space available, making it act closer to a strlcpy(). As several fixed strings are strcpy()'d after into the same buffer, their total length is checked to see if they exceed the remaining space in the buffer. If so, return an error. On the occasion simplify code a bit by removing unneeded rrqlen zeroing. Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/net/tftp.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c index 409b1d09b..336b78691 100644 --- a/grub-core/net/tftp.c +++ b/grub-core/net/tftp.c @@ -266,17 +266,19 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)), * forward slashes to a single forward slash. */ static void -grub_normalize_filename (char *normalized, const char *filename) +grub_normalize_filename (char *normalized, const char *filename, int c) { char *dest = normalized; const char *src = filename; - while (*src != '\0') + while (*src != '\0' && c > 0) { if (src[0] == '/' && src[1] == '/') src++; - else + else { + c--; *dest++ = *src++; + } } *dest = '\0'; } @@ -287,7 +289,7 @@ tftp_open (struct grub_file *file, const char *filename) struct tftphdr *tftph; char *rrq; int i; - int rrqlen; + int rrqlen, rrqsize; int hdrlen; grub_uint8_t open_data[1500]; struct grub_net_buff nb; @@ -315,37 +317,45 @@ tftp_open (struct grub_file *file, const char *filename) tftph = (struct tftphdr *) nb.data; - rrq = (char *) tftph->u.rrq; - rrqlen = 0; - tftph->opcode = grub_cpu_to_be16_compile_time (TFTP_RRQ); + rrq = (char *) tftph->u.rrq; + rrqsize = sizeof (tftph->u.rrq); + /* * Copy and normalize the filename to work-around issues on some TFTP * servers when file names are being matched for remapping. */ - grub_normalize_filename (rrq, filename); - rrqlen += grub_strlen (rrq) + 1; + grub_normalize_filename (rrq, filename, rrqsize); + + rrqlen = grub_strlen (rrq) + 1; rrq += grub_strlen (rrq) + 1; - grub_strcpy (rrq, "octet"); + /* Verify there is enough space for the remaining components. */ rrqlen += grub_strlen ("octet") + 1; + rrqlen += grub_strlen ("blksize") + 1; + rrqlen += grub_strlen ("1024") + 1; + rrqlen += grub_strlen ("tsize") + 1; + rrqlen += grub_strlen ("0") + 1; + + if (rrqlen >= rrqsize) { + grub_free (data); + return grub_error (GRUB_ERR_BAD_FILENAME, N_("filename too long")); + } + + grub_strcpy (rrq, "octet"); rrq += grub_strlen ("octet") + 1; grub_strcpy (rrq, "blksize"); - rrqlen += grub_strlen ("blksize") + 1; rrq += grub_strlen ("blksize") + 1; grub_strcpy (rrq, "1024"); - rrqlen += grub_strlen ("1024") + 1; rrq += grub_strlen ("1024") + 1; grub_strcpy (rrq, "tsize"); - rrqlen += grub_strlen ("tsize") + 1; rrq += grub_strlen ("tsize") + 1; grub_strcpy (rrq, "0"); - rrqlen += grub_strlen ("0") + 1; rrq += grub_strlen ("0") + 1; hdrlen = sizeof (tftph->opcode) + rrqlen; From 2c34af908ebf4856051ed29e46d88abd2b20387f Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 8 Mar 2024 22:47:20 +1100 Subject: [PATCH 281/402] video/readers/jpeg: Do not permit duplicate SOF0 markers in JPEG Otherwise a subsequent header could change the height and width allowing future OOB writes. Fixes: CVE-2024-45774 Reported-by: Nils Langius Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper --- grub-core/video/readers/jpeg.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/grub-core/video/readers/jpeg.c b/grub-core/video/readers/jpeg.c index ae634fd41..631a89356 100644 --- a/grub-core/video/readers/jpeg.c +++ b/grub-core/video/readers/jpeg.c @@ -339,6 +339,10 @@ grub_jpeg_decode_sof (struct grub_jpeg_data *data) if (grub_errno != GRUB_ERR_NONE) return grub_errno; + if (data->image_height != 0 || data->image_width != 0) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: cannot have duplicate SOF0 markers"); + if (grub_jpeg_get_byte (data) != 8) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: only 8-bit precision is supported"); From 500e5fdd82ca40412b0b73f5e5dda38e4a3af96d Mon Sep 17 00:00:00 2001 From: B Horn Date: Thu, 18 Apr 2024 15:59:26 +0100 Subject: [PATCH 282/402] kern/dl: Fix for an integer overflow in grub_dl_ref() It was possible to overflow the value of mod->ref_count, a signed integer, by repeatedly invoking insmod on an already loaded module. This led to a use-after-free. As once ref_count was overflowed it became possible to unload the module while there was still references to it. This resolves the issue by using grub_add() to check if the ref_count will overflow and then stops further increments. Further changes were also made to grub_dl_unref() to check for the underflow condition and the reference count was changed to an unsigned 64-bit integer. Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/commands/minicmd.c | 2 +- grub-core/kern/dl.c | 17 ++++++++++++----- include/grub/dl.h | 8 ++++---- util/misc.c | 4 ++-- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/grub-core/commands/minicmd.c b/grub-core/commands/minicmd.c index fa498931e..286290866 100644 --- a/grub-core/commands/minicmd.c +++ b/grub-core/commands/minicmd.c @@ -167,7 +167,7 @@ grub_mini_cmd_lsmod (struct grub_command *cmd __attribute__ ((unused)), { grub_dl_dep_t dep; - grub_printf ("%s\t%d\t\t", mod->name, mod->ref_count); + grub_printf ("%s\t%" PRIuGRUB_UINT64_T "\t\t", mod->name, mod->ref_count); for (dep = mod->dep; dep; dep = dep->next) { if (dep != mod->dep) diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index 8ad015b07..99bc12385 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -32,6 +32,7 @@ #include #include #include +#include #ifdef GRUB_MACHINE_EFI #include @@ -556,7 +557,7 @@ grub_dl_resolve_dependencies (grub_dl_t mod, Elf_Ehdr *e) return GRUB_ERR_NONE; } -int +grub_uint64_t grub_dl_ref (grub_dl_t mod) { grub_dl_dep_t dep; @@ -567,10 +568,13 @@ grub_dl_ref (grub_dl_t mod) for (dep = mod->dep; dep; dep = dep->next) grub_dl_ref (dep->mod); - return ++mod->ref_count; + if (grub_add (mod->ref_count, 1, &mod->ref_count)) + grub_fatal ("Module reference count overflow"); + + return mod->ref_count; } -int +grub_uint64_t grub_dl_unref (grub_dl_t mod) { grub_dl_dep_t dep; @@ -581,10 +585,13 @@ grub_dl_unref (grub_dl_t mod) for (dep = mod->dep; dep; dep = dep->next) grub_dl_unref (dep->mod); - return --mod->ref_count; + if (grub_sub (mod->ref_count, 1, &mod->ref_count)) + grub_fatal ("Module reference count underflow"); + + return mod->ref_count; } -int +grub_uint64_t grub_dl_ref_count (grub_dl_t mod) { if (mod == NULL) diff --git a/include/grub/dl.h b/include/grub/dl.h index 750fc8d3d..84509c5c1 100644 --- a/include/grub/dl.h +++ b/include/grub/dl.h @@ -174,7 +174,7 @@ typedef struct grub_dl_dep *grub_dl_dep_t; struct grub_dl { char *name; - int ref_count; + grub_uint64_t ref_count; int persistent; grub_dl_dep_t dep; grub_dl_segment_t segment; @@ -203,9 +203,9 @@ grub_dl_t EXPORT_FUNC(grub_dl_load) (const char *name); grub_dl_t grub_dl_load_core (void *addr, grub_size_t size); grub_dl_t EXPORT_FUNC(grub_dl_load_core_noinit) (void *addr, grub_size_t size); int EXPORT_FUNC(grub_dl_unload) (grub_dl_t mod); -extern int EXPORT_FUNC(grub_dl_ref) (grub_dl_t mod); -extern int EXPORT_FUNC(grub_dl_unref) (grub_dl_t mod); -extern int EXPORT_FUNC(grub_dl_ref_count) (grub_dl_t mod); +extern grub_uint64_t EXPORT_FUNC(grub_dl_ref) (grub_dl_t mod); +extern grub_uint64_t EXPORT_FUNC(grub_dl_unref) (grub_dl_t mod); +extern grub_uint64_t EXPORT_FUNC(grub_dl_ref_count) (grub_dl_t mod); extern grub_dl_t EXPORT_VAR(grub_dl_head); diff --git a/util/misc.c b/util/misc.c index d545212d9..0f928e5b4 100644 --- a/util/misc.c +++ b/util/misc.c @@ -190,14 +190,14 @@ grub_xputs_real (const char *str) void (*grub_xputs) (const char *str) = grub_xputs_real; -int +grub_uint64_t grub_dl_ref (grub_dl_t mod) { (void) mod; return 0; } -int +grub_uint64_t grub_dl_unref (grub_dl_t mod) { (void) mod; From d72208423dcabf9eb4a3bcb17b6b31888396bd49 Mon Sep 17 00:00:00 2001 From: B Horn Date: Fri, 1 Nov 2024 19:37:32 +0000 Subject: [PATCH 283/402] kern/dl: Use correct segment in grub_dl_set_mem_attrs() The previous code would never actually call grub_update_mem_attrs() as sh_info will always be zero for the sections that exist in memory. Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/kern/dl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index 99bc12385..acc6ccad6 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -668,7 +668,7 @@ grub_dl_set_mem_attrs (grub_dl_t mod, void *ehdr) for (seg = mod->segment; seg; seg = seg->next) /* Does this ELF section's index match GRUB DL segment? */ - if (seg->section == s->sh_info) + if (seg->section == i) break; /* No GRUB DL segment found for this ELF section, skip it. */ From 98ad84328dcabfa603dcf5bd217570aa6b4bdd99 Mon Sep 17 00:00:00 2001 From: B Horn Date: Thu, 7 Nov 2024 06:00:36 +0000 Subject: [PATCH 284/402] kern/dl: Check for the SHF_INFO_LINK flag in grub_dl_relocate_symbols() The grub_dl_relocate_symbols() iterates through the sections in an ELF looking for relocation sections. According to the spec [1] the SHF_INFO_LINK flag should be set if the sh_info field is meant to be a section index. [1] https://refspecs.linuxbase.org/elf/gabi4+/ch4.sheader.html Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/kern/dl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index acc6ccad6..de8c3aa8d 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -623,6 +623,9 @@ grub_dl_relocate_symbols (grub_dl_t mod, void *ehdr) grub_dl_segment_t seg; grub_err_t err; + if (!(s->sh_flags & SHF_INFO_LINK)) + continue; + /* Find the target segment. */ for (seg = mod->segment; seg; seg = seg->next) if (seg->section == s->sh_info) From 05be856a8c3aae41f5df90cab7796ab7ee34b872 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Fri, 22 Nov 2024 06:27:55 +0000 Subject: [PATCH 285/402] commands/extcmd: Missing check for failed allocation The grub_extcmd_dispatcher() calls grub_arg_list_alloc() to allocate a grub_arg_list struct but it does not verify the allocation was successful. In case of failed allocation the NULL state pointer can be accessed in parse_option() through grub_arg_parse() which may lead to a security issue. Fixes: CVE-2024-45775 Reported-by: Nils Langius Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper Reviewed-by: Alec Brown --- grub-core/commands/extcmd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/commands/extcmd.c b/grub-core/commands/extcmd.c index 90a5ca24a..c236be13a 100644 --- a/grub-core/commands/extcmd.c +++ b/grub-core/commands/extcmd.c @@ -49,6 +49,9 @@ grub_extcmd_dispatcher (struct grub_command *cmd, int argc, char **args, } state = grub_arg_list_alloc (ext, argc, args); + if (state == NULL) + return grub_errno; + if (grub_arg_parse (ext, argc, args, state, &new_args, &new_argc)) { context.state = state; From 0bf56bce47489c059e50e61a3db7f682d8c44b56 Mon Sep 17 00:00:00 2001 From: B Horn Date: Sun, 12 May 2024 11:08:23 +0100 Subject: [PATCH 286/402] commands/ls: Fix NULL dereference The grub_strrchr() may return NULL when the dirname do not contain "/". This can happen on broken filesystems. Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/commands/ls.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/grub-core/commands/ls.c b/grub-core/commands/ls.c index 6a1c7f5d3..f660946a2 100644 --- a/grub-core/commands/ls.c +++ b/grub-core/commands/ls.c @@ -241,7 +241,11 @@ grub_ls_list_files (char *dirname, int longlist, int all, int human) grub_file_close (file); - p = grub_strrchr (dirname, '/') + 1; + p = grub_strrchr (dirname, '/'); + if (p == NULL) + goto fail; + ++p; + ctx.dirname = grub_strndup (dirname, p - dirname); if (ctx.dirname == NULL) goto fail; From 2123c5bca7e21fbeb0263df4597ddd7054700726 Mon Sep 17 00:00:00 2001 From: B Horn Date: Fri, 1 Nov 2024 19:24:29 +0000 Subject: [PATCH 287/402] commands/pgp: Unregister the "check_signatures" hooks on module unload If the hooks are not removed they can be called after the module has been unloaded leading to an use-after-free. Fixes: CVE-2025-0622 Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/commands/pgp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grub-core/commands/pgp.c b/grub-core/commands/pgp.c index c6766f044..5fadc33c4 100644 --- a/grub-core/commands/pgp.c +++ b/grub-core/commands/pgp.c @@ -1010,6 +1010,8 @@ GRUB_MOD_INIT(pgp) GRUB_MOD_FINI(pgp) { + grub_register_variable_hook ("check_signatures", NULL, NULL); + grub_env_unset ("check_signatures"); grub_verifier_unregister (&grub_pubkey_verifier); grub_unregister_extcmd (cmd); grub_unregister_extcmd (cmd_trust); From 9c16197734ada8d0838407eebe081117799bfe67 Mon Sep 17 00:00:00 2001 From: B Horn Date: Fri, 1 Nov 2024 23:46:55 +0000 Subject: [PATCH 288/402] normal: Remove variables hooks on module unload The normal module does not entirely cleanup after itself in its GRUB_MOD_FINI() leaving a few variables hooks in place. It is not possible to unload normal module now but fix the issues for completeness. On the occasion replace 0s with NULLs for "pager" variable hooks unregister. Fixes: CVE-2025-0622 Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/normal/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index 838f57fa5..04d058f55 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -586,7 +586,9 @@ GRUB_MOD_FINI(normal) grub_xputs = grub_xputs_saved; grub_set_history (0); - grub_register_variable_hook ("pager", 0, 0); + grub_register_variable_hook ("pager", NULL, NULL); + grub_register_variable_hook ("color_normal", NULL, NULL); + grub_register_variable_hook ("color_highlight", NULL, NULL); grub_fs_autoload_hook = 0; grub_unregister_command (cmd_clear); } From 7580addfc8c94cedb0cdfd7a1fd65b539215e637 Mon Sep 17 00:00:00 2001 From: B Horn Date: Fri, 1 Nov 2024 23:52:06 +0000 Subject: [PATCH 289/402] gettext: Remove variables hooks on module unload The gettext module does not entirely cleanup after itself in its GRUB_MOD_FINI() leaving a few variables hooks in place. It is not possible to unload gettext module because normal module depends on it. Though fix the issues for completeness. Fixes: CVE-2025-0622 Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/gettext/gettext.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/grub-core/gettext/gettext.c b/grub-core/gettext/gettext.c index 7a1c14e4f..e4f4f8ee6 100644 --- a/grub-core/gettext/gettext.c +++ b/grub-core/gettext/gettext.c @@ -535,6 +535,10 @@ GRUB_MOD_INIT (gettext) GRUB_MOD_FINI (gettext) { + grub_register_variable_hook ("locale_dir", NULL, NULL); + grub_register_variable_hook ("secondary_locale_dir", NULL, NULL); + grub_register_variable_hook ("lang", NULL, NULL); + grub_gettext_delete_list (&main_context); grub_gettext_delete_list (&secondary_context); From 09bd6eb58b0f71ec273916070fa1e2de16897a91 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Fri, 22 Nov 2024 06:27:56 +0000 Subject: [PATCH 290/402] gettext: Integer overflow leads to heap OOB write or read Calculation of ctx->grub_gettext_msg_list size in grub_mofile_open() may overflow leading to subsequent OOB write or read. This patch fixes the issue by replacing grub_zalloc() and explicit multiplication with grub_calloc() which does the same thing in safe manner. Fixes: CVE-2024-45776 Reported-by: Nils Langius Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper Reviewed-by: Alec Brown --- grub-core/gettext/gettext.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/gettext/gettext.c b/grub-core/gettext/gettext.c index e4f4f8ee6..63bb1ab73 100644 --- a/grub-core/gettext/gettext.c +++ b/grub-core/gettext/gettext.c @@ -323,8 +323,8 @@ grub_mofile_open (struct grub_gettext_context *ctx, for (ctx->grub_gettext_max_log = 0; ctx->grub_gettext_max >> ctx->grub_gettext_max_log; ctx->grub_gettext_max_log++); - ctx->grub_gettext_msg_list = grub_zalloc (ctx->grub_gettext_max - * sizeof (ctx->grub_gettext_msg_list[0])); + ctx->grub_gettext_msg_list = grub_calloc (ctx->grub_gettext_max, + sizeof (ctx->grub_gettext_msg_list[0])); if (!ctx->grub_gettext_msg_list) { grub_file_close (fd); From b970a5ed967816bbca8225994cd0ee2557bad515 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Fri, 22 Nov 2024 06:27:57 +0000 Subject: [PATCH 291/402] gettext: Integer overflow leads to heap OOB write The size calculation of the translation buffer in grub_gettext_getstr_from_position() may overflow to 0 leading to heap OOB write. This patch fixes the issue by using grub_add() and checking for an overflow. Fixes: CVE-2024-45777 Reported-by: Nils Langius Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper Reviewed-by: Alec Brown --- grub-core/gettext/gettext.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/grub-core/gettext/gettext.c b/grub-core/gettext/gettext.c index 63bb1ab73..9ffc73428 100644 --- a/grub-core/gettext/gettext.c +++ b/grub-core/gettext/gettext.c @@ -26,6 +26,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -99,6 +100,7 @@ grub_gettext_getstr_from_position (struct grub_gettext_context *ctx, char *translation; struct string_descriptor desc; grub_err_t err; + grub_size_t alloc_sz; internal_position = (off + position * sizeof (desc)); @@ -109,7 +111,10 @@ grub_gettext_getstr_from_position (struct grub_gettext_context *ctx, length = grub_cpu_to_le32 (desc.length); offset = grub_cpu_to_le32 (desc.offset); - translation = grub_malloc (length + 1); + if (grub_add (length, 1, &alloc_sz)) + return NULL; + + translation = grub_malloc (alloc_sz); if (!translation) return NULL; From dad8f502974ed9ad0a70ae6820d17b4b142558fc Mon Sep 17 00:00:00 2001 From: Jonathan Bar Or Date: Thu, 23 Jan 2025 19:17:05 +0100 Subject: [PATCH 292/402] commands/read: Fix an integer overflow when supplying more than 2^31 characters The grub_getline() function currently has a signed integer variable "i" that can be overflown when user supplies more than 2^31 characters. It results in a memory corruption of the allocated line buffer as well as supplying large negative values to grub_realloc(). Fixes: CVE-2025-0690 Reported-by: Jonathan Bar Or Signed-off-by: Jonathan Bar Or Reviewed-by: Daniel Kiper --- grub-core/commands/read.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/grub-core/commands/read.c b/grub-core/commands/read.c index 597c90706..8d72e45c9 100644 --- a/grub-core/commands/read.c +++ b/grub-core/commands/read.c @@ -25,6 +25,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -37,13 +38,14 @@ static const struct grub_arg_option options[] = static char * grub_getline (int silent) { - int i; + grub_size_t i; char *line; char *tmp; int c; + grub_size_t alloc_size; i = 0; - line = grub_malloc (1 + i + sizeof('\0')); + line = grub_malloc (1 + sizeof('\0')); if (! line) return NULL; @@ -59,8 +61,17 @@ grub_getline (int silent) line[i] = (char) c; if (!silent) grub_printf ("%c", c); - i++; - tmp = grub_realloc (line, 1 + i + sizeof('\0')); + if (grub_add (i, 1, &i)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + return NULL; + } + if (grub_add (i, 1 + sizeof('\0'), &alloc_size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + return NULL; + } + tmp = grub_realloc (line, alloc_size); if (! tmp) { grub_free (line); From c68b7d23628a19da67ebe2e06f84165ee04961af Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Mon, 16 Dec 2024 20:22:41 +0000 Subject: [PATCH 293/402] commands/test: Stack overflow due to unlimited recursion depth The test_parse() evaluates test expression recursively. Due to lack of recursion depth check a specially crafted expression may cause a stack overflow. The recursion is only triggered by the parentheses usage and it can be unlimited. However, sensible expressions are unlikely to contain more than a few parentheses. So, this patch limits the recursion depth to 100, which should be sufficient. Reported-by: Nils Langius Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper --- grub-core/commands/test.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/grub-core/commands/test.c b/grub-core/commands/test.c index 62d3fb398..b585c3d70 100644 --- a/grub-core/commands/test.c +++ b/grub-core/commands/test.c @@ -29,6 +29,9 @@ GRUB_MOD_LICENSE ("GPLv3+"); +/* Set a limit on recursion to avoid stack overflow. */ +#define MAX_TEST_RECURSION_DEPTH 100 + /* A simple implementation for signed numbers. */ static int grub_strtosl (char *arg, const char ** const end, int base) @@ -150,7 +153,7 @@ get_fileinfo (char *path, struct test_parse_ctx *ctx) /* Parse a test expression starting from *argn. */ static int -test_parse (char **args, int *argn, int argc) +test_parse (char **args, int *argn, int argc, int *depth) { struct test_parse_ctx ctx = { .and = 1, @@ -387,13 +390,24 @@ test_parse (char **args, int *argn, int argc) if (grub_strcmp (args[*argn], ")") == 0) { (*argn)++; + if (*depth > 0) + (*depth)--; + return ctx.or || ctx.and; } /* Recursively invoke if parenthesis. */ if (grub_strcmp (args[*argn], "(") == 0) { (*argn)++; - update_val (test_parse (args, argn, argc), &ctx); + + if (++(*depth) > MAX_TEST_RECURSION_DEPTH) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("max recursion depth exceeded")); + depth--; + return ctx.or || ctx.and; + } + + update_val (test_parse (args, argn, argc, depth), &ctx); continue; } @@ -428,11 +442,12 @@ grub_cmd_test (grub_command_t cmd __attribute__ ((unused)), int argc, char **args) { int argn = 0; + int depth = 0; if (argc >= 1 && grub_strcmp (args[argc - 1], "]") == 0) argc--; - return test_parse (args, &argn, argc) ? GRUB_ERR_NONE + return test_parse (args, &argn, argc, &depth) ? GRUB_ERR_NONE : grub_error (GRUB_ERR_TEST_FAILURE, N_("false")); } From 34824806ac6302f91e8cabaa41308eaced25725f Mon Sep 17 00:00:00 2001 From: B Horn Date: Thu, 18 Apr 2024 20:29:39 +0100 Subject: [PATCH 294/402] commands/minicmd: Block the dump command in lockdown mode The dump enables a user to read memory which should not be possible in lockdown mode. Fixes: CVE-2025-1118 Reported-by: B Horn Reported-by: Jonathan Bar Or Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/commands/minicmd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/commands/minicmd.c b/grub-core/commands/minicmd.c index 286290866..8c5ee3e60 100644 --- a/grub-core/commands/minicmd.c +++ b/grub-core/commands/minicmd.c @@ -203,8 +203,8 @@ GRUB_MOD_INIT(minicmd) grub_register_command ("help", grub_mini_cmd_help, 0, N_("Show this message.")); cmd_dump = - grub_register_command ("dump", grub_mini_cmd_dump, - N_("ADDR [SIZE]"), N_("Show memory contents.")); + grub_register_command_lockdown ("dump", grub_mini_cmd_dump, + N_("ADDR [SIZE]"), N_("Show memory contents.")); cmd_rmmod = grub_register_command ("rmmod", grub_mini_cmd_rmmod, N_("MODULE"), N_("Remove a module.")); From 340e4d058f584534f4b90b7dbea2b64a9f8c418c Mon Sep 17 00:00:00 2001 From: B Horn Date: Thu, 18 Apr 2024 20:37:10 +0100 Subject: [PATCH 295/402] commands/memrw: Disable memory reading in lockdown mode With the rest of module being blocked in lockdown mode it does not make a lot of sense to leave memory reading enabled. This also goes in par with disabling the dump command. Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/commands/memrw.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/grub-core/commands/memrw.c b/grub-core/commands/memrw.c index d401a6db0..3542683d1 100644 --- a/grub-core/commands/memrw.c +++ b/grub-core/commands/memrw.c @@ -122,17 +122,20 @@ grub_cmd_write (grub_command_t cmd, int argc, char **argv) GRUB_MOD_INIT(memrw) { cmd_read_byte = - grub_register_extcmd ("read_byte", grub_cmd_read, 0, - N_("ADDR"), N_("Read 8-bit value from ADDR."), - options); + grub_register_extcmd_lockdown ("read_byte", grub_cmd_read, 0, + N_("ADDR"), + N_("Read 8-bit value from ADDR."), + options); cmd_read_word = - grub_register_extcmd ("read_word", grub_cmd_read, 0, - N_("ADDR"), N_("Read 16-bit value from ADDR."), - options); + grub_register_extcmd_lockdown ("read_word", grub_cmd_read, 0, + N_("ADDR"), + N_("Read 16-bit value from ADDR."), + options); cmd_read_dword = - grub_register_extcmd ("read_dword", grub_cmd_read, 0, - N_("ADDR"), N_("Read 32-bit value from ADDR."), - options); + grub_register_extcmd_lockdown ("read_dword", grub_cmd_read, 0, + N_("ADDR"), + N_("Read 32-bit value from ADDR."), + options); cmd_write_byte = grub_register_command_lockdown ("write_byte", grub_cmd_write, N_("ADDR VALUE [MASK]"), From 5f31164aed51f498957cdd6ed733ec71a8592c99 Mon Sep 17 00:00:00 2001 From: B Horn Date: Fri, 19 Apr 2024 22:31:45 +0100 Subject: [PATCH 296/402] commands/hexdump: Disable memory reading in lockdown mode Reported-by: B Horn Signed-off-by: B Horn Reviewed-by: Daniel Kiper --- grub-core/commands/hexdump.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/grub-core/commands/hexdump.c b/grub-core/commands/hexdump.c index eaa12465b..d6f61d98a 100644 --- a/grub-core/commands/hexdump.c +++ b/grub-core/commands/hexdump.c @@ -24,6 +24,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -51,7 +52,11 @@ grub_cmd_hexdump (grub_extcmd_context_t ctxt, int argc, char **args) length = (state[1].set) ? grub_strtoul (state[1].arg, 0, 0) : 256; if (!grub_strcmp (args[0], "(mem)")) - hexdump (skip, (char *) (grub_addr_t) skip, length); + { + if (grub_is_lockdown() == GRUB_LOCKDOWN_ENABLED) + return grub_error (GRUB_ERR_ACCESS_DENIED, N_("memory reading is disabled in lockdown mode")); + hexdump (skip, (char *) (grub_addr_t) skip, length); + } else if ((args[0][0] == '(') && (args[0][namelen - 1] == ')')) { grub_disk_t disk; From 26db6605036bd9e5b16d9068a8cc75be63b8b630 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Sat, 23 Mar 2024 15:59:43 +1100 Subject: [PATCH 297/402] fs/bfs: Disable under lockdown The BFS is not fuzz-clean. Don't allow it to be loaded under lockdown. This will also disable the AFS. Fixes: CVE-2024-45778 Fixes: CVE-2024-45779 Reported-by: Nils Langius Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper --- grub-core/fs/bfs.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/grub-core/fs/bfs.c b/grub-core/fs/bfs.c index 022f69fe2..78aeb051f 100644 --- a/grub-core/fs/bfs.c +++ b/grub-core/fs/bfs.c @@ -30,6 +30,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -1106,8 +1107,11 @@ GRUB_MOD_INIT (bfs) { COMPILE_TIME_ASSERT (1 << LOG_EXTENT_SIZE == sizeof (struct grub_bfs_extent)); - grub_bfs_fs.mod = mod; - grub_fs_register (&grub_bfs_fs); + if (!grub_is_lockdown ()) + { + grub_bfs_fs.mod = mod; + grub_fs_register (&grub_bfs_fs); + } } #ifdef MODE_AFS @@ -1116,5 +1120,6 @@ GRUB_MOD_FINI (afs) GRUB_MOD_FINI (bfs) #endif { - grub_fs_unregister (&grub_bfs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_bfs_fs); } From c4bc55da28543d2522a939ba4ee0acde45f2fa74 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Sat, 23 Mar 2024 16:20:45 +1100 Subject: [PATCH 298/402] fs: Disable many filesystems under lockdown The idea is to permit the following: btrfs, cpio, exfat, ext, f2fs, fat, hfsplus, iso9660, squash4, tar, xfs and zfs. The JFS, ReiserFS, romfs, UDF and UFS security vulnerabilities were reported by Jonathan Bar Or . Fixes: CVE-2025-0677 Fixes: CVE-2025-0684 Fixes: CVE-2025-0685 Fixes: CVE-2025-0686 Fixes: CVE-2025-0689 Suggested-by: Daniel Axtens Signed-off-by: Daniel Axtens Reviewed-by: Daniel Kiper --- grub-core/fs/affs.c | 11 ++++++++--- grub-core/fs/cbfs.c | 11 ++++++++--- grub-core/fs/jfs.c | 11 ++++++++--- grub-core/fs/minix.c | 11 ++++++++--- grub-core/fs/nilfs2.c | 11 ++++++++--- grub-core/fs/ntfs.c | 11 ++++++++--- grub-core/fs/reiserfs.c | 11 ++++++++--- grub-core/fs/romfs.c | 11 ++++++++--- grub-core/fs/sfs.c | 11 ++++++++--- grub-core/fs/udf.c | 11 ++++++++--- grub-core/fs/ufs.c | 11 ++++++++--- 11 files changed, 88 insertions(+), 33 deletions(-) diff --git a/grub-core/fs/affs.c b/grub-core/fs/affs.c index 9b0afb954..520a001c7 100644 --- a/grub-core/fs/affs.c +++ b/grub-core/fs/affs.c @@ -26,6 +26,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -703,12 +704,16 @@ static struct grub_fs grub_affs_fs = GRUB_MOD_INIT(affs) { - grub_affs_fs.mod = mod; - grub_fs_register (&grub_affs_fs); + if (!grub_is_lockdown ()) + { + grub_affs_fs.mod = mod; + grub_fs_register (&grub_affs_fs); + } my_mod = mod; } GRUB_MOD_FINI(affs) { - grub_fs_unregister (&grub_affs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_affs_fs); } diff --git a/grub-core/fs/cbfs.c b/grub-core/fs/cbfs.c index 2332745fe..b62c8777c 100644 --- a/grub-core/fs/cbfs.c +++ b/grub-core/fs/cbfs.c @@ -26,6 +26,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -390,13 +391,17 @@ GRUB_MOD_INIT (cbfs) #if (defined (__i386__) || defined (__x86_64__)) && !defined (GRUB_UTIL) && !defined (GRUB_MACHINE_EMU) && !defined (GRUB_MACHINE_XEN) init_cbfsdisk (); #endif - grub_cbfs_fs.mod = mod; - grub_fs_register (&grub_cbfs_fs); + if (!grub_is_lockdown ()) + { + grub_cbfs_fs.mod = mod; + grub_fs_register (&grub_cbfs_fs); + } } GRUB_MOD_FINI (cbfs) { - grub_fs_unregister (&grub_cbfs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_cbfs_fs); #if (defined (__i386__) || defined (__x86_64__)) && !defined (GRUB_UTIL) && !defined (GRUB_MACHINE_EMU) && !defined (GRUB_MACHINE_XEN) fini_cbfsdisk (); #endif diff --git a/grub-core/fs/jfs.c b/grub-core/fs/jfs.c index a82800ac3..03be9ef4c 100644 --- a/grub-core/fs/jfs.c +++ b/grub-core/fs/jfs.c @@ -26,6 +26,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -1004,12 +1005,16 @@ static struct grub_fs grub_jfs_fs = GRUB_MOD_INIT(jfs) { - grub_jfs_fs.mod = mod; - grub_fs_register (&grub_jfs_fs); + if (!grub_is_lockdown ()) + { + grub_jfs_fs.mod = mod; + grub_fs_register (&grub_jfs_fs); + } my_mod = mod; } GRUB_MOD_FINI(jfs) { - grub_fs_unregister (&grub_jfs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_jfs_fs); } diff --git a/grub-core/fs/minix.c b/grub-core/fs/minix.c index b7679c3e2..4440fcca8 100644 --- a/grub-core/fs/minix.c +++ b/grub-core/fs/minix.c @@ -25,6 +25,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -734,8 +735,11 @@ GRUB_MOD_INIT(minix) #endif #endif { - grub_minix_fs.mod = mod; - grub_fs_register (&grub_minix_fs); + if (!grub_is_lockdown ()) + { + grub_minix_fs.mod = mod; + grub_fs_register (&grub_minix_fs); + } my_mod = mod; } @@ -757,5 +761,6 @@ GRUB_MOD_FINI(minix) #endif #endif { - grub_fs_unregister (&grub_minix_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_minix_fs); } diff --git a/grub-core/fs/nilfs2.c b/grub-core/fs/nilfs2.c index 4e1e71738..26e6077ff 100644 --- a/grub-core/fs/nilfs2.c +++ b/grub-core/fs/nilfs2.c @@ -34,6 +34,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -1231,12 +1232,16 @@ GRUB_MOD_INIT (nilfs2) grub_nilfs2_dat_entry)); COMPILE_TIME_ASSERT (1 << LOG_INODE_SIZE == sizeof (struct grub_nilfs2_inode)); - grub_nilfs2_fs.mod = mod; - grub_fs_register (&grub_nilfs2_fs); + if (!grub_is_lockdown ()) + { + grub_nilfs2_fs.mod = mod; + grub_fs_register (&grub_nilfs2_fs); + } my_mod = mod; } GRUB_MOD_FINI (nilfs2) { - grub_fs_unregister (&grub_nilfs2_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_nilfs2_fs); } diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index 4e144cc3c..e00349b1d 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -27,6 +27,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -1541,12 +1542,16 @@ static struct grub_fs grub_ntfs_fs = GRUB_MOD_INIT (ntfs) { - grub_ntfs_fs.mod = mod; - grub_fs_register (&grub_ntfs_fs); + if (!grub_is_lockdown ()) + { + grub_ntfs_fs.mod = mod; + grub_fs_register (&grub_ntfs_fs); + } my_mod = mod; } GRUB_MOD_FINI (ntfs) { - grub_fs_unregister (&grub_ntfs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_ntfs_fs); } diff --git a/grub-core/fs/reiserfs.c b/grub-core/fs/reiserfs.c index c3850e013..5d3c85950 100644 --- a/grub-core/fs/reiserfs.c +++ b/grub-core/fs/reiserfs.c @@ -39,6 +39,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -1417,12 +1418,16 @@ static struct grub_fs grub_reiserfs_fs = GRUB_MOD_INIT(reiserfs) { - grub_reiserfs_fs.mod = mod; - grub_fs_register (&grub_reiserfs_fs); + if (!grub_is_lockdown ()) + { + grub_reiserfs_fs.mod = mod; + grub_fs_register (&grub_reiserfs_fs); + } my_mod = mod; } GRUB_MOD_FINI(reiserfs) { - grub_fs_unregister (&grub_reiserfs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_reiserfs_fs); } diff --git a/grub-core/fs/romfs.c b/grub-core/fs/romfs.c index 56b0b2b2f..eafab03b2 100644 --- a/grub-core/fs/romfs.c +++ b/grub-core/fs/romfs.c @@ -23,6 +23,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -475,11 +476,15 @@ static struct grub_fs grub_romfs_fs = GRUB_MOD_INIT(romfs) { - grub_romfs_fs.mod = mod; - grub_fs_register (&grub_romfs_fs); + if (!grub_is_lockdown ()) + { + grub_romfs_fs.mod = mod; + grub_fs_register (&grub_romfs_fs); + } } GRUB_MOD_FINI(romfs) { - grub_fs_unregister (&grub_romfs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_romfs_fs); } diff --git a/grub-core/fs/sfs.c b/grub-core/fs/sfs.c index f0d7cac43..88705b3a2 100644 --- a/grub-core/fs/sfs.c +++ b/grub-core/fs/sfs.c @@ -26,6 +26,7 @@ #include #include #include +#include #include GRUB_MOD_LICENSE ("GPLv3+"); @@ -779,12 +780,16 @@ static struct grub_fs grub_sfs_fs = GRUB_MOD_INIT(sfs) { - grub_sfs_fs.mod = mod; - grub_fs_register (&grub_sfs_fs); + if (!grub_is_lockdown ()) + { + grub_sfs_fs.mod = mod; + grub_fs_register (&grub_sfs_fs); + } my_mod = mod; } GRUB_MOD_FINI(sfs) { - grub_fs_unregister (&grub_sfs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_sfs_fs); } diff --git a/grub-core/fs/udf.c b/grub-core/fs/udf.c index 8765c633c..3d5ee5af5 100644 --- a/grub-core/fs/udf.c +++ b/grub-core/fs/udf.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -1455,12 +1456,16 @@ static struct grub_fs grub_udf_fs = { GRUB_MOD_INIT (udf) { - grub_udf_fs.mod = mod; - grub_fs_register (&grub_udf_fs); + if (!grub_is_lockdown ()) + { + grub_udf_fs.mod = mod; + grub_fs_register (&grub_udf_fs); + } my_mod = mod; } GRUB_MOD_FINI (udf) { - grub_fs_unregister (&grub_udf_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_udf_fs); } diff --git a/grub-core/fs/ufs.c b/grub-core/fs/ufs.c index e82d9356d..8b5adbd48 100644 --- a/grub-core/fs/ufs.c +++ b/grub-core/fs/ufs.c @@ -25,6 +25,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -899,8 +900,11 @@ GRUB_MOD_INIT(ufs1) #endif #endif { - grub_ufs_fs.mod = mod; - grub_fs_register (&grub_ufs_fs); + if (!grub_is_lockdown ()) + { + grub_ufs_fs.mod = mod; + grub_fs_register (&grub_ufs_fs); + } my_mod = mod; } @@ -914,6 +918,7 @@ GRUB_MOD_FINI(ufs1) #endif #endif { - grub_fs_unregister (&grub_ufs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_ufs_fs); } From c407724dad6c3e2fc1571e57adbda71cc03f82aa Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Wed, 22 Jan 2025 02:55:09 +0000 Subject: [PATCH 299/402] disk: Use safe math macros to prevent overflows Replace direct arithmetic operations with macros from include/grub/safemath.h to prevent potential overflow issues when calculating the memory sizes. Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- grub-core/disk/cryptodisk.c | 36 ++++++++++++++----- grub-core/disk/diskfilter.c | 9 +++-- grub-core/disk/ieee1275/obdisk.c | 43 +++++++++++++++++++---- grub-core/disk/ieee1275/ofdisk.c | 59 +++++++++++++++++++++++++++----- grub-core/disk/ldm.c | 36 ++++++++++++++++--- grub-core/disk/luks2.c | 7 +++- grub-core/disk/memdisk.c | 7 +++- grub-core/disk/plainmount.c | 9 +++-- 8 files changed, 172 insertions(+), 34 deletions(-) diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index 45adffdd9..431db2fae 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -28,6 +28,7 @@ #include #include #include +#include #ifdef GRUB_UTIL #include @@ -1654,7 +1655,7 @@ static char * luks_script_get (grub_size_t *sz) { grub_cryptodisk_t i; - grub_size_t size = 0; + grub_size_t size = 0, mul; char *ptr, *ret; *sz = 0; @@ -1663,10 +1664,6 @@ luks_script_get (grub_size_t *sz) if (grub_strcmp (i->modname, "luks") == 0 || grub_strcmp (i->modname, "luks2") == 0) { - size += grub_strlen (i->modname); - size += sizeof ("_mount"); - size += grub_strlen (i->uuid); - size += grub_strlen (i->cipher->cipher->name); /* * Add space in the line for (in order) spaces, cipher mode, cipher IV * mode, sector offset, sector size and the trailing newline. This is @@ -1674,14 +1671,35 @@ luks_script_get (grub_size_t *sz) * in an earlier version of this code that are unaccounted for. It is * left in the calculations in case it is needed. At worst, its short- * lived wasted space. + * + * 60 = 5 + 5 + 8 + 20 + 6 + 1 + 15 */ - size += 5 + 5 + 8 + 20 + 6 + 1 + 15; + if (grub_add (size, grub_strlen (i->modname), &size) || + grub_add (size, sizeof ("_mount") + 60, &size) || + grub_add (size, grub_strlen (i->uuid), &size) || + grub_add (size, grub_strlen (i->cipher->cipher->name), &size) || + grub_mul (i->keysize, 2, &mul) || + grub_add (size, mul, &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while obtaining size of luks script"); + return 0; + } if (i->essiv_hash) - size += grub_strlen (i->essiv_hash->name); - size += i->keysize * 2; + { + if (grub_add (size, grub_strlen (i->essiv_hash->name), &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while obtaining size of luks script"); + return 0; + } + } } + if (grub_add (size, 1, &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while obtaining size of luks script"); + return 0; + } - ret = grub_malloc (size + 1); + ret = grub_malloc (size); if (!ret) return 0; diff --git a/grub-core/disk/diskfilter.c b/grub-core/disk/diskfilter.c index 606195c26..78d6a15db 100644 --- a/grub-core/disk/diskfilter.c +++ b/grub-core/disk/diskfilter.c @@ -24,6 +24,7 @@ #include #include #include +#include #ifdef GRUB_UTIL #include #include @@ -1052,7 +1053,7 @@ grub_diskfilter_make_raid (grub_size_t uuidlen, char *uuid, int nmemb, { struct grub_diskfilter_vg *array; int i; - grub_size_t j; + grub_size_t j, sz; grub_uint64_t totsize; struct grub_diskfilter_pv *pv; grub_err_t err; @@ -1153,7 +1154,11 @@ grub_diskfilter_make_raid (grub_size_t uuidlen, char *uuid, int nmemb, } array->lvs->vg = array; - array->lvs->idname = grub_malloc (sizeof ("mduuid/") + 2 * uuidlen); + if (grub_mul (uuidlen, 2, &sz) || + grub_add (sz, sizeof ("mduuid/"), &sz)) + goto fail; + + array->lvs->idname = grub_malloc (sz); if (!array->lvs->idname) goto fail; diff --git a/grub-core/disk/ieee1275/obdisk.c b/grub-core/disk/ieee1275/obdisk.c index cd923b90f..9d4c42665 100644 --- a/grub-core/disk/ieee1275/obdisk.c +++ b/grub-core/disk/ieee1275/obdisk.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -128,9 +129,17 @@ count_commas (const char *src) static char * decode_grub_devname (const char *name) { - char *devpath = grub_malloc (grub_strlen (name) + 1); + char *devpath; char *p, c; + grub_size_t sz; + if (grub_add (grub_strlen (name), 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining size of device name")); + return NULL; + } + + devpath = grub_malloc (sz); if (devpath == NULL) return NULL; @@ -156,12 +165,20 @@ static char * encode_grub_devname (const char *path) { char *encoding, *optr; + grub_size_t sz; if (path == NULL) return NULL; - encoding = grub_malloc (sizeof (IEEE1275_DEV) + count_commas (path) + - grub_strlen (path) + 1); + if (grub_add (sizeof (IEEE1275_DEV) + 1, count_commas (path), &sz) || + grub_add (sz, grub_strlen (path), &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining encoding size")); + grub_print_error (); + return NULL; + } + + encoding = grub_malloc (sz); if (encoding == NULL) { @@ -396,6 +413,14 @@ canonicalise_disk (const char *devname) real_unit_str_len = grub_strlen (op->name) + sizeof (IEEE1275_DISK_ALIAS) + grub_strlen (real_unit_address); + if (grub_add (grub_strlen (op->name), sizeof (IEEE1275_DISK_ALIAS), &real_unit_str_len) || + grub_add (real_unit_str_len, grub_strlen (real_unit_address), &real_unit_str_len)) + { + grub_free (parent); + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining size of canonical name")); + grub_print_error (); + return NULL; + } real_canon = grub_malloc (real_unit_str_len); @@ -413,6 +438,7 @@ canonicalise_disk (const char *devname) static struct disk_dev * add_canon_disk (const char *cname) { + grub_size_t sz; struct disk_dev *dev; dev = grub_zalloc (sizeof (struct disk_dev)); @@ -428,13 +454,18 @@ add_canon_disk (const char *cname) * arguments and allows a client program to open * the entire (raw) disk. Any disk label is ignored. */ - dev->raw_name = grub_malloc (grub_strlen (cname) + sizeof (":nolabel")); + if (grub_add (grub_strlen (cname), sizeof (":nolabel"), &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while appending :nolabel to end of canonical name"); + goto failed; + } + + dev->raw_name = grub_malloc (sz); if (dev->raw_name == NULL) goto failed; - grub_snprintf (dev->raw_name, grub_strlen (cname) + sizeof (":nolabel"), - "%s:nolabel", cname); + grub_snprintf (dev->raw_name, sz, "%s:nolabel", cname); } /* diff --git a/grub-core/disk/ieee1275/ofdisk.c b/grub-core/disk/ieee1275/ofdisk.c index c6cba0c8a..4c5b89cbc 100644 --- a/grub-core/disk/ieee1275/ofdisk.c +++ b/grub-core/disk/ieee1275/ofdisk.c @@ -24,6 +24,7 @@ #include #include #include +#include static char *last_devpath; static grub_ieee1275_ihandle_t last_ihandle; @@ -80,6 +81,7 @@ ofdisk_hash_add_real (char *devpath) struct ofdisk_hash_ent **head = &ofdisk_hash[ofdisk_hash_fn(devpath)]; const char *iptr; char *optr; + grub_size_t sz; p = grub_zalloc (sizeof (*p)); if (!p) @@ -87,8 +89,14 @@ ofdisk_hash_add_real (char *devpath) p->devpath = devpath; - p->grub_devpath = grub_malloc (sizeof ("ieee1275/") - + 2 * grub_strlen (p->devpath)); + if (grub_mul (grub_strlen (p->devpath), 2, &sz) || + grub_add (sz, sizeof ("ieee1275/"), &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining size of device path")); + return NULL; + } + + p->grub_devpath = grub_malloc (sz); if (!p->grub_devpath) { @@ -98,7 +106,13 @@ ofdisk_hash_add_real (char *devpath) if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_PARTITION_0)) { - p->open_path = grub_malloc (grub_strlen (p->devpath) + 3); + 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")); + return NULL; + } + + p->open_path = grub_malloc (sz); if (!p->open_path) { grub_free (p->grub_devpath); @@ -224,6 +238,7 @@ dev_iterate (const struct grub_ieee1275_devalias *alias) args; char *buf, *bufptr; unsigned i; + grub_size_t sz; if (grub_ieee1275_open (alias->path, &ihandle)) return; @@ -243,7 +258,14 @@ dev_iterate (const struct grub_ieee1275_devalias *alias) return; } - buf = grub_malloc (grub_strlen (alias->path) + 32); + if (grub_add (grub_strlen (alias->path), 32, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while creating buffer for vscsi"); + grub_ieee1275_close (ihandle); + return; + } + + buf = grub_malloc (sz); if (!buf) return; bufptr = grub_stpcpy (buf, alias->path); @@ -287,9 +309,15 @@ dev_iterate (const struct grub_ieee1275_devalias *alias) grub_uint64_t *table; grub_uint16_t table_size; grub_ieee1275_ihandle_t ihandle; + grub_size_t sz; - buf = grub_malloc (grub_strlen (alias->path) + - sizeof ("/disk@7766554433221100")); + if (grub_add (grub_strlen (alias->path), sizeof ("/disk@7766554433221100"), &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while creating buffer for sas_ioa"); + return; + } + + buf = grub_malloc (sz); if (!buf) return; bufptr = grub_stpcpy (buf, alias->path); @@ -427,9 +455,17 @@ grub_ofdisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, static char * compute_dev_path (const char *name) { - char *devpath = grub_malloc (grub_strlen (name) + 3); + char *devpath; char *p, c; + grub_size_t sz; + if (grub_add (grub_strlen (name), 3, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining size of device path")); + return NULL; + } + + devpath = grub_malloc (sz); if (!devpath) return NULL; @@ -625,6 +661,7 @@ insert_bootpath (void) char *bootpath; grub_ssize_t bootpath_size; char *type; + grub_size_t sz; if (grub_ieee1275_get_property_length (grub_ieee1275_chosen, "bootpath", &bootpath_size) @@ -635,7 +672,13 @@ insert_bootpath (void) return; } - bootpath = (char *) grub_malloc ((grub_size_t) bootpath_size + 64); + if (grub_add (bootpath_size, 64, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining bootpath size")); + return; + } + + bootpath = (char *) grub_malloc (sz); if (! bootpath) { grub_print_error (); diff --git a/grub-core/disk/ldm.c b/grub-core/disk/ldm.c index 34bfe6bd1..4101b15d8 100644 --- a/grub-core/disk/ldm.c +++ b/grub-core/disk/ldm.c @@ -220,6 +220,7 @@ make_vg (grub_disk_t disk, struct grub_ldm_vblk vblk[GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_ldm_vblk)]; unsigned i; + grub_size_t sz; err = grub_disk_read (disk, cursec, 0, sizeof(vblk), &vblk); if (err) @@ -251,7 +252,13 @@ make_vg (grub_disk_t disk, grub_free (pv); goto fail2; } - pv->internal_id = grub_malloc (ptr[0] + 2); + if (grub_add (ptr[0], 2, &sz)) + { + grub_free (pv); + goto fail2; + } + + pv->internal_id = grub_malloc (sz); if (!pv->internal_id) { grub_free (pv); @@ -276,7 +283,15 @@ make_vg (grub_disk_t disk, goto fail2; } pv->id.uuidlen = *ptr; - pv->id.uuid = grub_malloc (pv->id.uuidlen + 1); + + if (grub_add (pv->id.uuidlen, 1, &sz)) + { + grub_free (pv->internal_id); + grub_free (pv); + goto fail2; + } + + pv->id.uuid = grub_malloc (sz); grub_memcpy (pv->id.uuid, ptr + 1, pv->id.uuidlen); pv->id.uuid[pv->id.uuidlen] = 0; @@ -343,7 +358,13 @@ make_vg (grub_disk_t disk, grub_free (lv); goto fail2; } - lv->internal_id = grub_malloc ((grub_size_t) ptr[0] + 2); + if (grub_add (ptr[0], 2, &sz)) + { + grub_free (lv->segments); + grub_free (lv); + goto fail2; + } + lv->internal_id = grub_malloc (sz); if (!lv->internal_id) { grub_free (lv); @@ -455,6 +476,7 @@ make_vg (grub_disk_t disk, struct grub_ldm_vblk vblk[GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_ldm_vblk)]; unsigned i; + grub_size_t sz; err = grub_disk_read (disk, cursec, 0, sizeof(vblk), &vblk); if (err) @@ -490,7 +512,12 @@ make_vg (grub_disk_t disk, grub_free (comp); goto fail2; } - comp->internal_id = grub_malloc ((grub_size_t) ptr[0] + 2); + if (grub_add (ptr[0], 2, &sz)) + { + grub_free (comp); + goto fail2; + } + comp->internal_id = grub_malloc (sz); if (!comp->internal_id) { grub_free (comp); @@ -640,7 +667,6 @@ make_vg (grub_disk_t disk, if (lv->segments->node_alloc == lv->segments->node_count) { void *t; - grub_size_t sz; if (grub_mul (lv->segments->node_alloc, 2, &lv->segments->node_alloc) || grub_mul (lv->segments->node_alloc, sizeof (*lv->segments->nodes), &sz)) diff --git a/grub-core/disk/luks2.c b/grub-core/disk/luks2.c index d5106402f..8036d76ff 100644 --- a/grub-core/disk/luks2.c +++ b/grub-core/disk/luks2.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -569,6 +570,7 @@ luks2_recover_key (grub_disk_t source, gcry_err_code_t gcry_ret; grub_json_t *json = NULL, keyslots; grub_err_t ret; + grub_size_t sz; if (cargs->key_data == NULL || cargs->key_len == 0) return grub_error (GRUB_ERR_BAD_ARGUMENT, "no key data"); @@ -577,7 +579,10 @@ luks2_recover_key (grub_disk_t source, if (ret) return ret; - json_header = grub_zalloc (grub_be_to_cpu64 (header.hdr_size) - sizeof (header)); + if (grub_sub (grub_be_to_cpu64 (header.hdr_size), sizeof (header), &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "underflow detected while calculating json header size"); + + json_header = grub_zalloc (sz); if (!json_header) return GRUB_ERR_OUT_OF_MEMORY; diff --git a/grub-core/disk/memdisk.c b/grub-core/disk/memdisk.c index 613779cf3..36de3bfab 100644 --- a/grub-core/disk/memdisk.c +++ b/grub-core/disk/memdisk.c @@ -23,6 +23,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -96,7 +97,11 @@ GRUB_MOD_INIT(memdisk) grub_dprintf ("memdisk", "Found memdisk image at %p\n", memdisk_orig_addr); - memdisk_size = header->size - sizeof (struct grub_module_header); + if (grub_sub (header->size, sizeof (struct grub_module_header), &memdisk_size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "underflow detected while obtaining memdisk size"); + return; + } memdisk_addr = grub_malloc (memdisk_size); grub_dprintf ("memdisk", "Copying memdisk image to dynamic memory\n"); diff --git a/grub-core/disk/plainmount.c b/grub-core/disk/plainmount.c index 47e64805f..21ec4072c 100644 --- a/grub-core/disk/plainmount.c +++ b/grub-core/disk/plainmount.c @@ -24,6 +24,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -126,7 +127,7 @@ plainmount_configure_password (grub_cryptodisk_t dev, const char *hash, grub_uint8_t *derived_hash, *dh; char *p; unsigned int round, i, len, size; - grub_size_t alloc_size; + grub_size_t alloc_size, sz; grub_err_t err = GRUB_ERR_NONE; /* Support none (plain) hash */ @@ -145,7 +146,11 @@ plainmount_configure_password (grub_cryptodisk_t dev, const char *hash, * Allocate buffer for the password and for an added prefix character * for each hash round ('alloc_size' may not be a multiple of 'len'). */ - p = grub_zalloc (alloc_size + (alloc_size / len) + 1); + if (grub_add (alloc_size, (alloc_size / len), &sz) || + grub_add (sz, 1, &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while allocating size of password buffer")); + + p = grub_zalloc (sz); derived_hash = grub_zalloc (GRUB_CRYPTODISK_MAX_KEYLEN * 2); if (p == NULL || derived_hash == NULL) { From d8151f98331ee4d15fcca59edffa59246d8fc15f Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Wed, 22 Jan 2025 02:55:10 +0000 Subject: [PATCH 300/402] disk: Prevent overflows when allocating memory for arrays Use grub_calloc() when allocating memory for arrays to ensure proper overflow checks are in place. Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- grub-core/disk/cryptodisk.c | 2 +- grub-core/disk/lvm.c | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index 431db2fae..7a785a49c 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -1532,7 +1532,7 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) if (state[OPTION_PROTECTOR].set) /* key protector(s) */ { - cargs.key_cache = grub_zalloc (state[OPTION_PROTECTOR].set * sizeof (*cargs.key_cache)); + cargs.key_cache = grub_calloc (state[OPTION_PROTECTOR].set, sizeof (*cargs.key_cache)); if (cargs.key_cache == NULL) return grub_error (GRUB_ERR_OUT_OF_MEMORY, "no memory for key protector key cache"); diff --git a/grub-core/disk/lvm.c b/grub-core/disk/lvm.c index 0c32c95f9..b53c3b75e 100644 --- a/grub-core/disk/lvm.c +++ b/grub-core/disk/lvm.c @@ -671,8 +671,7 @@ grub_lvm_detect (grub_disk_t disk, goto lvs_segment_fail; } - seg->nodes = grub_zalloc (sizeof (seg->nodes[0]) - * seg->node_count); + seg->nodes = grub_calloc (seg->node_count, sizeof (seg->nodes[0])); p = grub_strstr (p, "mirrors = ["); if (p == NULL) @@ -760,8 +759,7 @@ grub_lvm_detect (grub_disk_t disk, } } - seg->nodes = grub_zalloc (sizeof (seg->nodes[0]) - * seg->node_count); + seg->nodes = grub_calloc (seg->node_count, sizeof (seg->nodes[0])); p = grub_strstr (p, "raids = ["); if (p == NULL) From 33bd6b5ac5c77b346769ab5284262f94e695e464 Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Wed, 22 Jan 2025 02:55:11 +0000 Subject: [PATCH 301/402] disk: Check if returned pointer for allocated memory is NULL When using grub_malloc(), grub_zalloc() or grub_calloc(), these functions can fail if we are out of memory. After allocating memory we should check if these functions returned NULL and handle this error if they did. On the occasion make a NULL check in ATA code more obvious. Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- grub-core/disk/ata.c | 4 ++-- grub-core/disk/ieee1275/obdisk.c | 6 ++++++ grub-core/disk/ldm.c | 6 ++++++ grub-core/disk/lvm.c | 14 ++++++++++++++ grub-core/disk/memdisk.c | 2 ++ 5 files changed, 30 insertions(+), 2 deletions(-) diff --git a/grub-core/disk/ata.c b/grub-core/disk/ata.c index 7b6ac7bfc..a2433e29e 100644 --- a/grub-core/disk/ata.c +++ b/grub-core/disk/ata.c @@ -112,10 +112,10 @@ grub_ata_identify (struct grub_ata *dev) return grub_atapi_identify (dev); info64 = grub_malloc (GRUB_DISK_SECTOR_SIZE); + if (info64 == NULL) + return grub_errno; info32 = (grub_uint32_t *) info64; info16 = (grub_uint16_t *) info64; - if (! info16) - return grub_errno; grub_memset (&parms, 0, sizeof (parms)); parms.buffer = info16; diff --git a/grub-core/disk/ieee1275/obdisk.c b/grub-core/disk/ieee1275/obdisk.c index 9d4c42665..fcc39e0a2 100644 --- a/grub-core/disk/ieee1275/obdisk.c +++ b/grub-core/disk/ieee1275/obdisk.c @@ -423,6 +423,12 @@ canonicalise_disk (const char *devname) } real_canon = grub_malloc (real_unit_str_len); + if (real_canon == NULL) + { + grub_free (parent); + grub_print_error (); + return NULL; + } grub_snprintf (real_canon, real_unit_str_len, "%s/disk@%s", op->name, real_unit_address); diff --git a/grub-core/disk/ldm.c b/grub-core/disk/ldm.c index 4101b15d8..048e29cd0 100644 --- a/grub-core/disk/ldm.c +++ b/grub-core/disk/ldm.c @@ -292,6 +292,12 @@ make_vg (grub_disk_t disk, } pv->id.uuid = grub_malloc (sz); + if (pv->id.uuid == NULL) + { + grub_free (pv->internal_id); + grub_free (pv); + goto fail2; + } grub_memcpy (pv->id.uuid, ptr + 1, pv->id.uuidlen); pv->id.uuid[pv->id.uuidlen] = 0; diff --git a/grub-core/disk/lvm.c b/grub-core/disk/lvm.c index b53c3b75e..d5af85482 100644 --- a/grub-core/disk/lvm.c +++ b/grub-core/disk/lvm.c @@ -370,6 +370,8 @@ grub_lvm_detect (grub_disk_t disk, break; pv = grub_zalloc (sizeof (*pv)); + if (pv == NULL) + goto fail4; q = p; while (*q != ' ' && q < mda_end) q++; @@ -379,6 +381,8 @@ grub_lvm_detect (grub_disk_t disk, s = q - p; pv->name = grub_malloc (s + 1); + if (pv->name == NULL) + goto pvs_fail_noname; grub_memcpy (pv->name, p, s); pv->name[s] = '\0'; @@ -451,6 +455,8 @@ grub_lvm_detect (grub_disk_t disk, break; lv = grub_zalloc (sizeof (*lv)); + if (lv == NULL) + goto fail4; q = p; while (*q != ' ' && q < mda_end) @@ -545,6 +551,8 @@ grub_lvm_detect (grub_disk_t disk, goto lvs_fail; } lv->segments = grub_calloc (lv->segment_count, sizeof (*seg)); + if (lv->segments == NULL) + goto lvs_fail; seg = lv->segments; for (i = 0; i < lv->segment_count; i++) @@ -612,6 +620,8 @@ grub_lvm_detect (grub_disk_t disk, seg->nodes = grub_calloc (seg->node_count, sizeof (*stripe)); + if (seg->nodes == NULL) + goto lvs_segment_fail; stripe = seg->nodes; p = grub_strstr (p, "stripes = ["); @@ -672,6 +682,8 @@ grub_lvm_detect (grub_disk_t disk, } seg->nodes = grub_calloc (seg->node_count, sizeof (seg->nodes[0])); + if (seg->nodes == NULL) + goto lvs_segment_fail; p = grub_strstr (p, "mirrors = ["); if (p == NULL) @@ -760,6 +772,8 @@ grub_lvm_detect (grub_disk_t disk, } seg->nodes = grub_calloc (seg->node_count, sizeof (seg->nodes[0])); + if (seg->nodes == NULL) + goto lvs_segment_fail; p = grub_strstr (p, "raids = ["); if (p == NULL) diff --git a/grub-core/disk/memdisk.c b/grub-core/disk/memdisk.c index 36de3bfab..2d7afaea3 100644 --- a/grub-core/disk/memdisk.c +++ b/grub-core/disk/memdisk.c @@ -103,6 +103,8 @@ GRUB_MOD_INIT(memdisk) return; } memdisk_addr = grub_malloc (memdisk_size); + if (memdisk_addr == NULL) + return; grub_dprintf ("memdisk", "Copying memdisk image to dynamic memory\n"); grub_memmove (memdisk_addr, memdisk_orig_addr, memdisk_size); From fbaddcca541805c333f0fc792b82772594e73753 Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Wed, 22 Jan 2025 02:55:12 +0000 Subject: [PATCH 302/402] disk/ieee1275/ofdisk: Call grub_ieee1275_close() when grub_malloc() fails In the dev_iterate() function a handle is opened but isn't closed when grub_malloc() returns NULL. We should fix this by closing it on error. Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- grub-core/disk/ieee1275/ofdisk.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/grub-core/disk/ieee1275/ofdisk.c b/grub-core/disk/ieee1275/ofdisk.c index 4c5b89cbc..dbc0f1aba 100644 --- a/grub-core/disk/ieee1275/ofdisk.c +++ b/grub-core/disk/ieee1275/ofdisk.c @@ -267,7 +267,10 @@ dev_iterate (const struct grub_ieee1275_devalias *alias) buf = grub_malloc (sz); if (!buf) - return; + { + grub_ieee1275_close (ihandle); + return; + } bufptr = grub_stpcpy (buf, alias->path); for (i = 0; i < args.nentries; i++) From 6608163b08a7a8be4b0ab2a5cd4593bba07fe2b7 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Tue, 21 Jan 2025 19:02:36 +0000 Subject: [PATCH 303/402] fs: Use safe math macros to prevent overflows Replace direct arithmetic operations with macros from include/grub/safemath.h to prevent potential overflow issues when calculating the memory sizes. Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper --- grub-core/fs/archelp.c | 9 ++++++++- grub-core/fs/btrfs.c | 34 ++++++++++++++++++++++++++++------ grub-core/fs/cpio_common.c | 16 ++++++++++++++-- grub-core/fs/erofs.c | 9 +++++++-- grub-core/fs/f2fs.c | 17 +++++++++++++++-- grub-core/fs/ntfscomp.c | 9 ++++++++- grub-core/fs/squash4.c | 12 +++++++++--- grub-core/fs/xfs.c | 17 +++++++++++++++-- 8 files changed, 104 insertions(+), 19 deletions(-) diff --git a/grub-core/fs/archelp.c b/grub-core/fs/archelp.c index c1dcc6285..0816b28de 100644 --- a/grub-core/fs/archelp.c +++ b/grub-core/fs/archelp.c @@ -21,6 +21,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -68,6 +69,7 @@ handle_symlink (struct grub_archelp_data *data, char *rest; char *linktarget; grub_size_t linktarget_len; + grub_size_t sz; *restart = 0; @@ -98,7 +100,12 @@ handle_symlink (struct grub_archelp_data *data, if (linktarget[0] == '\0') return GRUB_ERR_NONE; linktarget_len = grub_strlen (linktarget); - target = grub_malloc (linktarget_len + grub_strlen (*name) + 2); + + if (grub_add (linktarget_len, grub_strlen (*name), &sz) || + grub_add (sz, 2, &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("link target length overflow")); + + target = grub_malloc (sz); if (!target) return grub_errno; diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index aae81482b..0625b1166 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -1801,6 +1801,7 @@ find_path (struct grub_btrfs_data *data, char *path_alloc = NULL; char *origpath = NULL; unsigned symlinks_max = 32; + grub_size_t sz; err = get_root (data, key, tree, type); if (err) @@ -1891,9 +1892,15 @@ find_path (struct grub_btrfs_data *data, struct grub_btrfs_dir_item *cdirel; if (elemsize > allocated) { - allocated = 2 * elemsize; + if (grub_mul (2, elemsize, &allocated) || + grub_add (allocated, 1, &sz)) + { + grub_free (path_alloc); + grub_free (origpath); + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("directory item size overflow")); + } grub_free (direl); - direl = grub_malloc (allocated + 1); + direl = grub_malloc (sz); if (!direl) { grub_free (path_alloc); @@ -1955,8 +1962,16 @@ find_path (struct grub_btrfs_data *data, grub_free (origpath); return err; } - tmp = grub_malloc (grub_le_to_cpu64 (inode.size) - + grub_strlen (path) + 1); + + if (grub_add (grub_le_to_cpu64 (inode.size), grub_strlen (path), &sz) || + grub_add (sz, 1, &sz)) + { + grub_free (direl); + grub_free (path_alloc); + grub_free (origpath); + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("buffer size overflow")); + } + tmp = grub_malloc (sz); if (!tmp) { grub_free (direl); @@ -2078,6 +2093,7 @@ grub_btrfs_dir (grub_device_t device, const char *path, grub_uint64_t tree; grub_uint8_t type; grub_size_t est_size = 0; + grub_size_t sz; if (!data) return grub_errno; @@ -2119,9 +2135,15 @@ grub_btrfs_dir (grub_device_t device, const char *path, } if (elemsize > allocated) { - allocated = 2 * elemsize; + if (grub_mul (2, elemsize, &allocated) || + grub_add (allocated, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("directory element size overflow")); + r = -grub_errno; + break; + } grub_free (direl); - direl = grub_malloc (allocated + 1); + direl = grub_malloc (sz); if (!direl) { r = -grub_errno; diff --git a/grub-core/fs/cpio_common.c b/grub-core/fs/cpio_common.c index 5d41b6fdb..6ba58b354 100644 --- a/grub-core/fs/cpio_common.c +++ b/grub-core/fs/cpio_common.c @@ -24,6 +24,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -48,6 +49,7 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name, struct head hd; grub_size_t namesize; grub_uint32_t modeval; + grub_size_t sz; data->hofs = data->next_hofs; @@ -76,7 +78,10 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name, *mode = modeval; - *name = grub_malloc (namesize + 1); + if (grub_add (namesize, 1, &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("file name size overflow")); + + *name = grub_malloc (sz); if (*name == NULL) return grub_errno; @@ -110,10 +115,17 @@ grub_cpio_get_link_target (struct grub_archelp_data *data) { char *ret; grub_err_t err; + grub_size_t sz; if (data->size == 0) return grub_strdup (""); - ret = grub_malloc (data->size + 1); + + if (grub_add (data->size, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("target data size overflow")); + return NULL; + } + ret = grub_malloc (sz); if (!ret) return NULL; diff --git a/grub-core/fs/erofs.c b/grub-core/fs/erofs.c index ae38b045e..82a05051d 100644 --- a/grub-core/fs/erofs.c +++ b/grub-core/fs/erofs.c @@ -681,7 +681,7 @@ static char * erofs_read_symlink (grub_fshelp_node_t node) { char *symlink; - grub_size_t sz; + grub_size_t sz, lsz; grub_err_t err; if (node->inode_loaded == false) @@ -699,7 +699,12 @@ erofs_read_symlink (grub_fshelp_node_t node) return NULL; } - symlink = grub_malloc (sz + 1); + if (grub_add (sz, 1, &lsz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("symlink size overflow")); + return NULL; + } + symlink = grub_malloc (lsz); if (symlink == NULL) return NULL; diff --git a/grub-core/fs/f2fs.c b/grub-core/fs/f2fs.c index f6d6beaa5..72b4aa1e6 100644 --- a/grub-core/fs/f2fs.c +++ b/grub-core/fs/f2fs.c @@ -28,6 +28,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -958,6 +959,7 @@ grub_f2fs_read_symlink (grub_fshelp_node_t node) char *symlink; struct grub_fshelp_node *diro = node; grub_uint64_t filesize; + grub_size_t sz; if (!diro->inode_read) { @@ -968,7 +970,12 @@ grub_f2fs_read_symlink (grub_fshelp_node_t node) filesize = grub_f2fs_file_size(&diro->inode.i); - symlink = grub_malloc (filesize + 1); + if (grub_add (filesize, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("symlink size overflow")); + return 0; + } + symlink = grub_malloc (sz); if (!symlink) return 0; @@ -997,6 +1004,7 @@ grub_f2fs_check_dentries (struct grub_f2fs_dir_iter_ctx *ctx) enum FILE_TYPE ftype; int name_len; int ret; + int sz; if (grub_f2fs_test_bit_le (i, ctx->bitmap) == 0) { @@ -1010,7 +1018,12 @@ grub_f2fs_check_dentries (struct grub_f2fs_dir_iter_ctx *ctx) if (name_len >= F2FS_NAME_LEN) return 0; - filename = grub_malloc (name_len + 1); + if (grub_add (name_len, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("directory entry name length overflow")); + return 0; + } + filename = grub_malloc (sz); if (!filename) return 0; diff --git a/grub-core/fs/ntfscomp.c b/grub-core/fs/ntfscomp.c index a009f2c2d..f168a318e 100644 --- a/grub-core/fs/ntfscomp.c +++ b/grub-core/fs/ntfscomp.c @@ -22,6 +22,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -310,6 +311,7 @@ ntfscomp (grub_uint8_t *dest, grub_disk_addr_t ofs, { grub_err_t ret; grub_disk_addr_t vcn; + int log_sz; if (ctx->attr->sbuf) { @@ -349,7 +351,12 @@ ntfscomp (grub_uint8_t *dest, grub_disk_addr_t ofs, } ctx->comp.comp_head = ctx->comp.comp_tail = 0; - ctx->comp.cbuf = grub_malloc (1 << (ctx->comp.log_spc + GRUB_NTFS_BLK_SHR)); + if (grub_add (ctx->comp.log_spc, GRUB_NTFS_BLK_SHR, &log_sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("compression buffer size overflow")); + return 0; + } + ctx->comp.cbuf = grub_malloc (1 << log_sz); if (!ctx->comp.cbuf) return 0; diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index 6e9d63874..f91ff3bfa 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -460,11 +460,11 @@ grub_squash_read_symlink (grub_fshelp_node_t node) { char *ret; grub_err_t err; - grub_size_t sz; + grub_uint32_t sz; if (grub_add (grub_le_to_cpu32 (node->ino.symlink.namelen), 1, &sz)) { - grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("symlink name length overflow")); return NULL; } @@ -580,6 +580,7 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir, struct grub_squash_dirent di; struct grub_squash_inode ino; grub_size_t sz; + grub_uint16_t nlen; err = read_chunk (dir->data, &di, sizeof (di), grub_le_to_cpu64 (dir->data->sb.diroffset) @@ -595,7 +596,12 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir, if (err) return 0; - buf = grub_malloc (grub_le_to_cpu16 (di.namelen) + 2); + if (grub_add (grub_le_to_cpu16 (di.namelen), 2, &nlen)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("name length overflow")); + return 0; + } + buf = grub_malloc (nlen); if (!buf) return 0; err = read_chunk (dir->data, buf, diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index 732c4aaf3..ab1281497 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -718,6 +718,7 @@ static char * grub_xfs_read_symlink (grub_fshelp_node_t node) { grub_ssize_t size = grub_be_to_cpu64 (node->inode.size); + grub_size_t sz; if (size < 0) { @@ -739,7 +740,12 @@ grub_xfs_read_symlink (grub_fshelp_node_t node) if (node->data->hascrc) off = 56; - symlink = grub_malloc (size + 1); + if (grub_add (size, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("symlink size overflow")); + return 0; + } + symlink = grub_malloc (sz); if (!symlink) return 0; @@ -789,8 +795,15 @@ static int iterate_dir_call_hook (grub_uint64_t ino, const char *filename, { struct grub_fshelp_node *fdiro; grub_err_t err; + grub_size_t sz; - fdiro = grub_malloc (grub_xfs_fshelp_size(ctx->diro->data) + 1); + if (grub_add (grub_xfs_fshelp_size(ctx->diro->data), 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("directory data size overflow")); + grub_print_error (); + return 0; + } + fdiro = grub_malloc (sz); if (!fdiro) { grub_print_error (); From 84bc0a9a68835952ae69165c11709811dae7634e Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Tue, 21 Jan 2025 19:02:37 +0000 Subject: [PATCH 304/402] fs: Prevent overflows when allocating memory for arrays Use grub_calloc() when allocating memory for arrays to ensure proper overflow checks are in place. The HFS+ and squash4 security vulnerabilities were reported by Jonathan Bar Or . Fixes: CVE-2025-0678 Fixes: CVE-2025-1125 Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper --- grub-core/fs/btrfs.c | 4 ++-- grub-core/fs/hfspluscomp.c | 9 +++++++-- grub-core/fs/squash4.c | 8 ++++---- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 0625b1166..9c1e925c9 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -1276,8 +1276,8 @@ grub_btrfs_mount (grub_device_t dev) } data->n_devices_allocated = 16; - data->devices_attached = grub_malloc (sizeof (data->devices_attached[0]) - * data->n_devices_allocated); + data->devices_attached = grub_calloc (data->n_devices_allocated, + sizeof (data->devices_attached[0])); if (!data->devices_attached) { grub_free (data); diff --git a/grub-core/fs/hfspluscomp.c b/grub-core/fs/hfspluscomp.c index 48ae438d8..a80954ee6 100644 --- a/grub-core/fs/hfspluscomp.c +++ b/grub-core/fs/hfspluscomp.c @@ -244,14 +244,19 @@ hfsplus_open_compressed_real (struct grub_hfsplus_file *node) return 0; } node->compress_index_size = grub_le_to_cpu32 (index_size); - node->compress_index = grub_malloc (node->compress_index_size - * sizeof (node->compress_index[0])); + node->compress_index = grub_calloc (node->compress_index_size, + sizeof (node->compress_index[0])); if (!node->compress_index) { node->compressed = 0; grub_free (attr_node); return grub_errno; } + + /* + * The node->compress_index_size * sizeof (node->compress_index[0]) is safe here + * due to relevant checks done in grub_calloc() above. + */ if (grub_hfsplus_read_file (node, 0, 0, 0x104 + sizeof (index_size), node->compress_index_size diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index f91ff3bfa..cf2bca822 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -822,10 +822,10 @@ direct_read (struct grub_squash_data *data, break; } total_blocks = ((total_size + data->blksz - 1) >> data->log2_blksz); - ino->block_sizes = grub_malloc (total_blocks - * sizeof (ino->block_sizes[0])); - ino->cumulated_block_sizes = grub_malloc (total_blocks - * sizeof (ino->cumulated_block_sizes[0])); + ino->block_sizes = grub_calloc (total_blocks, + sizeof (ino->block_sizes[0])); + ino->cumulated_block_sizes = grub_calloc (total_blocks, + sizeof (ino->cumulated_block_sizes[0])); if (!ino->block_sizes || !ino->cumulated_block_sizes) { grub_free (ino->block_sizes); From cde9f7f338f8f5771777f0e7dfc423ddf952ad31 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Tue, 21 Jan 2025 19:02:38 +0000 Subject: [PATCH 305/402] fs: Prevent overflows when assigning returned values from read_number() The direct assignment of the unsigned long long value returned by read_number() can potentially lead to an overflow on a 32-bit systems. The fix replaces the direct assignments with calls to grub_cast() which detects the overflows and safely assigns the values if no overflow is detected. Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper --- grub-core/fs/cpio_common.c | 18 ++++++++++++++---- grub-core/fs/tar.c | 23 ++++++++++++++++------- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/grub-core/fs/cpio_common.c b/grub-core/fs/cpio_common.c index 6ba58b354..45ac119a8 100644 --- a/grub-core/fs/cpio_common.c +++ b/grub-core/fs/cpio_common.c @@ -62,11 +62,21 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name, #endif ) return grub_error (GRUB_ERR_BAD_FS, "invalid cpio archive"); - data->size = read_number (hd.filesize, ARRAY_SIZE (hd.filesize)); + + if (grub_cast (read_number (hd.filesize, ARRAY_SIZE (hd.filesize)), &data->size)) + return grub_error (GRUB_ERR_BAD_FS, N_("data size overflow")); + if (mtime) - *mtime = read_number (hd.mtime, ARRAY_SIZE (hd.mtime)); - modeval = read_number (hd.mode, ARRAY_SIZE (hd.mode)); - namesize = read_number (hd.namesize, ARRAY_SIZE (hd.namesize)); + { + if (grub_cast (read_number (hd.mtime, ARRAY_SIZE (hd.mtime)), mtime)) + return grub_error (GRUB_ERR_BAD_FS, N_("mtime overflow")); + } + + if (grub_cast (read_number (hd.mode, ARRAY_SIZE (hd.mode)), &modeval)) + return grub_error (GRUB_ERR_BAD_FS, N_("mode overflow")); + + if (grub_cast (read_number (hd.namesize, ARRAY_SIZE (hd.namesize)), &namesize)) + return grub_error (GRUB_ERR_BAD_FS, N_("namesize overflow")); /* Don't allow negative numbers. */ if (namesize >= 0x80000000) diff --git a/grub-core/fs/tar.c b/grub-core/fs/tar.c index fd2ec1f74..1eaa5349f 100644 --- a/grub-core/fs/tar.c +++ b/grub-core/fs/tar.c @@ -99,9 +99,10 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name, if (hd.typeflag == 'L') { grub_err_t err; - grub_size_t namesize = read_number (hd.size, sizeof (hd.size)); + grub_size_t namesize; - if (grub_add (namesize, 1, &sz)) + if (grub_cast (read_number (hd.size, sizeof (hd.size)), &namesize) || + grub_add (namesize, 1, &sz)) return grub_error (GRUB_ERR_BAD_FS, N_("name size overflow")); *name = grub_malloc (sz); @@ -123,9 +124,10 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name, if (hd.typeflag == 'K') { grub_err_t err; - grub_size_t linksize = read_number (hd.size, sizeof (hd.size)); + grub_size_t linksize; - if (grub_add (linksize, 1, &sz)) + if (grub_cast (read_number (hd.size, sizeof (hd.size)), &linksize) || + grub_add (linksize, 1, &sz)) return grub_error (GRUB_ERR_BAD_FS, N_("link size overflow")); if (data->linkname_alloc < sz) @@ -174,15 +176,22 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name, (*name)[extra_size + sizeof (hd.name)] = 0; } - data->size = read_number (hd.size, sizeof (hd.size)); + if (grub_cast (read_number (hd.size, sizeof (hd.size)), &data->size)) + return grub_error (GRUB_ERR_BAD_FS, N_("data size overflow")); + data->dofs = data->hofs + GRUB_DISK_SECTOR_SIZE; data->next_hofs = data->dofs + ((data->size + GRUB_DISK_SECTOR_SIZE - 1) & ~(GRUB_DISK_SECTOR_SIZE - 1)); if (mtime) - *mtime = read_number (hd.mtime, sizeof (hd.mtime)); + { + if (grub_cast (read_number (hd.mtime, sizeof (hd.mtime)), mtime)) + return grub_error (GRUB_ERR_BAD_FS, N_("mtime overflow")); + } if (mode) { - *mode = read_number (hd.mode, sizeof (hd.mode)); + if (grub_cast (read_number (hd.mode, sizeof (hd.mode)), mode)) + return grub_error (GRUB_ERR_BAD_FS, N_("mode overflow")); + switch (hd.typeflag) { /* Hardlink. */ From 88e491a0f744c6b19b6d4caa300a576ba56db7c9 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Wed, 22 Jan 2025 07:17:02 +0000 Subject: [PATCH 306/402] fs/zfs: Use safe math macros to prevent overflows Replace direct arithmetic operations with macros from include/grub/safemath.h to prevent potential overflow issues when calculating the memory sizes. Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper --- grub-core/fs/zfs/zfs.c | 50 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index 22ced4014..c77ab7ad3 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -2427,6 +2427,7 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap, zap_dnode->endian) << DNODE_SHIFT); grub_err_t err; grub_zfs_endian_t endian; + grub_size_t sz; if (zap_verify (zap, zap_dnode->endian)) return 0; @@ -2488,8 +2489,14 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap, if (le->le_type != ZAP_CHUNK_ENTRY) continue; - buf = grub_malloc (grub_zfs_to_cpu16 (le->le_name_length, endian) - * name_elem_length + 1); + if (grub_mul (grub_zfs_to_cpu16 (le->le_name_length, endian), name_elem_length, &sz) || + grub_add (sz, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("buffer size overflow")); + grub_free (l); + return grub_errno; + } + buf = grub_malloc (sz); if (zap_leaf_array_get (l, endian, blksft, grub_zfs_to_cpu16 (le->le_name_chunk, endian), @@ -2912,6 +2919,7 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn, && ((grub_zfs_to_cpu64(((znode_phys_t *) DN_BONUS (&dnode_path->dn.dn))->zp_mode, dnode_path->dn.endian) >> 12) & 0xf) == 0xa) { char *sym_value; + grub_size_t sz; grub_size_t sym_sz; int free_symval = 0; char *oldpath = path, *oldpathbuf = path_buf; @@ -2963,7 +2971,18 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn, break; free_symval = 1; } - path = path_buf = grub_malloc (sym_sz + grub_strlen (oldpath) + 1); + + if (grub_add (sym_sz, grub_strlen (oldpath), &sz) || + grub_add (sz, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("path buffer size overflow")); + grub_free (oldpathbuf); + if (free_symval) + grub_free (sym_value); + err = grub_errno; + break; + } + path = path_buf = grub_malloc (sz); if (!path_buf) { grub_free (oldpathbuf); @@ -3000,6 +3019,7 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn, { void *sahdrp; int hdrsize; + grub_size_t sz; if (dnode_path->dn.dn.dn_bonuslen != 0) { @@ -3033,7 +3053,15 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn, + SA_SIZE_OFFSET), dnode_path->dn.endian); char *oldpath = path, *oldpathbuf = path_buf; - path = path_buf = grub_malloc (sym_sz + grub_strlen (oldpath) + 1); + if (grub_add (sym_sz, grub_strlen (oldpath), &sz) || + grub_add (sz, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("path buffer size overflow")); + grub_free (oldpathbuf); + err = grub_errno; + break; + } + path = path_buf = grub_malloc (sz); if (!path_buf) { grub_free (oldpathbuf); @@ -3608,6 +3636,7 @@ grub_zfs_nvlist_lookup_nvlist_array (const char *nvlist, const char *name, unsigned i; grub_size_t nelm; int elemsize = 0; + int sz; found = nvlist_find_value (nvlist, name, DATA_TYPE_NVLIST_ARRAY, &nvpair, &size, &nelm); @@ -3642,7 +3671,12 @@ grub_zfs_nvlist_lookup_nvlist_array (const char *nvlist, const char *name, return 0; } - ret = grub_zalloc (elemsize + sizeof (grub_uint32_t)); + if (grub_add (elemsize, sizeof (grub_uint32_t), &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("elemsize overflow")); + return 0; + } + ret = grub_zalloc (sz); if (!ret) return 0; grub_memcpy (ret, nvlist, sizeof (grub_uint32_t)); @@ -4233,6 +4267,7 @@ iterate_zap_snap (const char *name, grub_uint64_t val, struct grub_dirhook_info info; char *name2; int ret; + grub_size_t sz; dnode_end_t mdn; @@ -4253,7 +4288,10 @@ iterate_zap_snap (const char *name, grub_uint64_t val, return 0; } - name2 = grub_malloc (grub_strlen (name) + 2); + if (grub_add (grub_strlen (name), 2, &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("name length overflow")); + + name2 = grub_malloc (sz); name2[0] = '@'; grub_memcpy (name2 + 1, name, grub_strlen (name) + 1); ret = ctx->hook (name2, &info, ctx->hook_data); From 7f38e32c7ebeaebb79e2c71e3c7d5ea367d3a39c Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Wed, 22 Jan 2025 07:17:03 +0000 Subject: [PATCH 307/402] fs/zfs: Prevent overflows when allocating memory for arrays Use grub_calloc() when allocating memory for arrays to ensure proper overflow checks are in place. Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper --- grub-core/fs/zfs/zfs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index c77ab7ad3..474122ed2 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -763,8 +763,8 @@ fill_vdev_info_real (struct grub_zfs_data *data, { fill->n_children = nelm; - fill->children = grub_zalloc (fill->n_children - * sizeof (fill->children[0])); + fill->children = grub_calloc (fill->n_children, + sizeof (fill->children[0])); } for (i = 0; i < nelm; i++) @@ -3752,8 +3752,8 @@ zfs_mount (grub_device_t dev) #endif data->n_devices_allocated = 16; - data->devices_attached = grub_malloc (sizeof (data->devices_attached[0]) - * data->n_devices_allocated); + data->devices_attached = grub_calloc (data->n_devices_allocated, + sizeof (data->devices_attached[0])); data->n_devices_attached = 0; err = scan_disk (dev, data, 1, &inserted); if (err) From 13065f69dae0eeb60813809026de5bd021051892 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Wed, 22 Jan 2025 07:17:01 +0000 Subject: [PATCH 308/402] fs/zfs: Check if returned pointer for allocated memory is NULL When using grub_malloc() or grub_zalloc(), these functions can fail if we are out of memory. After allocating memory we should check if these functions returned NULL and handle this error if they did. Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper --- grub-core/fs/zfs/zfs.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index 474122ed2..6a964974f 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -654,6 +654,8 @@ zfs_fetch_nvlist (struct grub_zfs_device_desc *diskdesc, char **nvlist) return grub_error (GRUB_ERR_BUG, "member drive unknown"); *nvlist = grub_malloc (VDEV_PHYS_SIZE); + if (!*nvlist) + return grub_errno; /* Read in the vdev name-value pair list (112K). */ err = grub_disk_read (diskdesc->dev->disk, diskdesc->vdev_phys_sector, 0, @@ -765,6 +767,11 @@ fill_vdev_info_real (struct grub_zfs_data *data, fill->children = grub_calloc (fill->n_children, sizeof (fill->children[0])); + if (!fill->children) + { + grub_free (type); + return grub_errno; + } } for (i = 0; i < nelm; i++) @@ -2497,6 +2504,11 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap, return grub_errno; } buf = grub_malloc (sz); + if (!buf) + { + grub_free (l); + return grub_errno; + } if (zap_leaf_array_get (l, endian, blksft, grub_zfs_to_cpu16 (le->le_name_chunk, endian), @@ -2512,6 +2524,12 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap, val_length = ((int) le->le_value_length * (int) le->le_int_size); val = grub_malloc (grub_zfs_to_cpu16 (val_length, endian)); + if (!val) + { + grub_free (l); + grub_free (buf); + return grub_errno; + } if (zap_leaf_array_get (l, endian, blksft, grub_zfs_to_cpu16 (le->le_value_chunk, endian), @@ -3754,6 +3772,11 @@ zfs_mount (grub_device_t dev) data->n_devices_allocated = 16; data->devices_attached = grub_calloc (data->n_devices_allocated, sizeof (data->devices_attached[0])); + if (!data->devices_attached) + { + grub_free (data); + return NULL; + } data->n_devices_attached = 0; err = scan_disk (dev, data, 1, &inserted); if (err) @@ -4292,6 +4315,9 @@ iterate_zap_snap (const char *name, grub_uint64_t val, return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("name length overflow")); name2 = grub_malloc (sz); + if (!name2) + return grub_errno; + name2[0] = '@'; grub_memcpy (name2 + 1, name, grub_strlen (name) + 1); ret = ctx->hook (name2, &info, ctx->hook_data); From dd6a4c8d10e02ca5056681e75795041a343636e4 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Wed, 22 Jan 2025 07:17:04 +0000 Subject: [PATCH 309/402] fs/zfs: Add missing NULL check after grub_strdup() call Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper --- grub-core/fs/zfs/zfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index 6a964974f..376042631 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -3349,6 +3349,8 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol, filename = 0; snapname = 0; fsname = grub_strdup (fullpath); + if (!fsname) + return grub_errno; } else { From 4beeff8a31c4fb4071d2225533cfa316b5a58391 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Wed, 22 Jan 2025 18:04:42 +0000 Subject: [PATCH 310/402] net: Use safe math macros to prevent overflows Replace direct arithmetic operations with macros from include/grub/safemath.h to prevent potential overflow issues when calculating the memory sizes. Signed-off-by: Lidong Chen Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- grub-core/net/bootp.c | 16 ++++++++-- grub-core/net/dns.c | 9 +++++- grub-core/net/drivers/ieee1275/ofnet.c | 20 ++++++++++-- grub-core/net/net.c | 43 +++++++++++++++++++++----- 4 files changed, 75 insertions(+), 13 deletions(-) diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index abe45ef7b..2f45a3cc2 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -24,6 +24,7 @@ #include #include #include +#include struct grub_dhcp_discover_options { @@ -686,6 +687,7 @@ grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)), unsigned num; const grub_uint8_t *ptr; grub_uint8_t taglength; + grub_uint8_t len; if (argc < 4) return grub_error (GRUB_ERR_BAD_ARGUMENT, @@ -727,7 +729,12 @@ grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)), if (grub_strcmp (args[3], "string") == 0) { grub_err_t err = GRUB_ERR_NONE; - char *val = grub_malloc (taglength + 1); + char *val; + + if (grub_add (taglength, 1, &len)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("tag length overflow")); + + val = grub_malloc (len); if (!val) return grub_errno; grub_memcpy (val, ptr, taglength); @@ -760,7 +767,12 @@ grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)), if (grub_strcmp (args[3], "hex") == 0) { grub_err_t err = GRUB_ERR_NONE; - char *val = grub_malloc (2 * taglength + 1); + char *val; + + if (grub_mul (taglength, 2, &len) || grub_add (len, 1, &len)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("tag length overflow")); + + val = grub_malloc (len); int i; if (!val) return grub_errno; diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c index fcc09aa65..39b0c46cf 100644 --- a/grub-core/net/dns.c +++ b/grub-core/net/dns.c @@ -224,10 +224,17 @@ get_name (const grub_uint8_t *name_at, const grub_uint8_t *head, { int length; char *ret; + int len; if (!check_name_real (name_at, head, tail, NULL, &length, NULL)) return NULL; - ret = grub_malloc (length + 1); + + if (grub_add (length, 1, &len)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("name length overflow")); + return NULL; + } + ret = grub_malloc (len); if (!ret) return NULL; if (!check_name_real (name_at, head, tail, NULL, NULL, ret)) diff --git a/grub-core/net/drivers/ieee1275/ofnet.c b/grub-core/net/drivers/ieee1275/ofnet.c index 3bf48b3f0..3e1b9094e 100644 --- a/grub-core/net/drivers/ieee1275/ofnet.c +++ b/grub-core/net/drivers/ieee1275/ofnet.c @@ -22,6 +22,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -387,6 +388,7 @@ search_net_devices (struct grub_ieee1275_devalias *alias) grub_uint8_t *pprop; char *shortname; char need_suffix = 1; + grub_size_t sz; if (grub_strcmp (alias->type, "network") != 0) return 0; @@ -444,9 +446,23 @@ search_net_devices (struct grub_ieee1275_devalias *alias) } if (need_suffix) - ofdata->path = grub_malloc (grub_strlen (alias->path) + sizeof (SUFFIX)); + { + if (grub_add (grub_strlen (alias->path), sizeof (SUFFIX), &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obatining size of ofdata path")); + grub_print_error (); + return 0; + } + } else - ofdata->path = grub_malloc (grub_strlen (alias->path) + 1); + { + if (grub_add (grub_strlen (alias->path), 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obatining size of ofdata path")); + grub_print_error (); + return 0; + } + } if (!ofdata->path) { grub_print_error (); diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 9939ff601..3ca7e0796 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -32,6 +32,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -206,6 +207,7 @@ grub_net_ipv6_get_slaac (struct grub_net_card *card, { struct grub_net_slaac_mac_list *slaac; char *ptr; + grub_size_t sz; for (slaac = card->slaac_list; slaac; slaac = slaac->next) if (grub_net_hwaddr_cmp (&slaac->address, hwaddr) == 0) @@ -215,9 +217,16 @@ grub_net_ipv6_get_slaac (struct grub_net_card *card, if (!slaac) return NULL; - slaac->name = grub_malloc (grub_strlen (card->name) - + GRUB_NET_MAX_STR_HWADDR_LEN - + sizeof (":slaac")); + if (grub_add (grub_strlen (card->name), + (GRUB_NET_MAX_STR_HWADDR_LEN + sizeof (":slaac")), &sz)) + { + grub_free (slaac); + grub_error (GRUB_ERR_OUT_OF_RANGE, + "overflow detected while obtaining size of slaac name"); + return NULL; + } + + slaac->name = grub_malloc (sz); ptr = grub_stpcpy (slaac->name, card->name); if (grub_net_hwaddr_cmp (&card->default_address, hwaddr) != 0) { @@ -288,6 +297,7 @@ grub_net_ipv6_get_link_local (struct grub_net_card *card, char *name; char *ptr; grub_net_network_level_address_t addr; + grub_size_t sz; addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; addr.ipv6[0] = grub_cpu_to_be64_compile_time (0xfe80ULL << 48); @@ -302,9 +312,14 @@ grub_net_ipv6_get_link_local (struct grub_net_card *card, return inf; } - name = grub_malloc (grub_strlen (card->name) - + GRUB_NET_MAX_STR_HWADDR_LEN - + sizeof (":link")); + if (grub_add (grub_strlen (card->name), + (GRUB_NET_MAX_STR_HWADDR_LEN + sizeof (":link")), &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, + "overflow detected while obtaining size of link name"); + return NULL; + } + name = grub_malloc (sz); if (!name) return NULL; @@ -1434,9 +1449,15 @@ grub_net_open_real (const char *name) if (grub_strchr (port_start + 1, ':')) { int iplen = grub_strlen (server); + grub_size_t sz; /* Bracket bare IPv6 addr. */ - host = grub_malloc (iplen + 3); + if (grub_add (iplen, 3, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining length of host")); + return NULL; + } + host = grub_malloc (sz); if (!host) return NULL; @@ -1691,6 +1712,7 @@ grub_env_set_net_property (const char *intername, const char *suffix, { char *varname, *varvalue; char *ptr; + grub_size_t sz; varname = grub_xasprintf ("net_%s_%s", intername, suffix); if (!varname) @@ -1698,7 +1720,12 @@ grub_env_set_net_property (const char *intername, const char *suffix, for (ptr = varname; *ptr; ptr++) if (*ptr == ':') *ptr = '_'; - varvalue = grub_malloc (len + 1); + if (grub_add (len, 1, &sz)) + { + grub_free (varname); + return grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while obtaining the size of an env variable"); + } + varvalue = grub_malloc (sz); if (!varvalue) { grub_free (varname); From dee2c14fd66bc497cdc74c69fde8c9b84637c8eb Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Wed, 22 Jan 2025 18:04:43 +0000 Subject: [PATCH 311/402] net: Prevent overflows when allocating memory for arrays Use grub_calloc() when allocating memory for arrays to ensure proper overflow checks are in place. Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper --- grub-core/net/dns.c | 4 ++-- grub-core/net/net.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c index 39b0c46cf..f20cd6f83 100644 --- a/grub-core/net/dns.c +++ b/grub-core/net/dns.c @@ -470,8 +470,8 @@ grub_net_dns_lookup (const char *name, && grub_get_time_ms () < dns_cache[h].limit_time) { grub_dprintf ("dns", "retrieved from cache\n"); - *addresses = grub_malloc (dns_cache[h].naddresses - * sizeof ((*addresses)[0])); + *addresses = grub_calloc (dns_cache[h].naddresses, + sizeof ((*addresses)[0])); if (!*addresses) return grub_errno; *naddresses = dns_cache[h].naddresses; diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 3ca7e0796..1abdc097f 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -88,8 +88,8 @@ grub_net_link_layer_add_address (struct grub_net_card *card, /* Add sender to cache table. */ if (card->link_layer_table == NULL) { - card->link_layer_table = grub_zalloc (LINK_LAYER_CACHE_SIZE - * sizeof (card->link_layer_table[0])); + card->link_layer_table = grub_calloc (LINK_LAYER_CACHE_SIZE, + sizeof (card->link_layer_table[0])); if (card->link_layer_table == NULL) return; } From 1c06ec900591d1fab6fbacf80dc010541d0a5ec8 Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Wed, 22 Jan 2025 18:04:44 +0000 Subject: [PATCH 312/402] net: Check if returned pointer for allocated memory is NULL When using grub_malloc(), the function can fail if we are out of memory. After allocating memory we should check if this function returned NULL and handle this error if it did. Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- grub-core/net/net.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 1abdc097f..6ea33d1cd 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -227,6 +227,11 @@ grub_net_ipv6_get_slaac (struct grub_net_card *card, } slaac->name = grub_malloc (sz); + if (slaac->name == NULL) + { + grub_free (slaac); + return NULL; + } ptr = grub_stpcpy (slaac->name, card->name); if (grub_net_hwaddr_cmp (&card->default_address, hwaddr) != 0) { From e3c578a56f9294e286b6028ca7c1def997a17b15 Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Tue, 28 Jan 2025 05:15:50 +0000 Subject: [PATCH 313/402] fs/sfs: Check if allocated memory is NULL When using grub_zalloc(), if we are out of memory, this function can fail. After allocating memory, we should check if grub_zalloc() returns NULL. If so, we should handle this error. Fixes: CID 473856 Signed-off-by: Alec Brown Reviewed-by: Ross Philipson Reviewed-by: Daniel Kiper --- grub-core/fs/sfs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/fs/sfs.c b/grub-core/fs/sfs.c index 88705b3a2..bad4ae8d1 100644 --- a/grub-core/fs/sfs.c +++ b/grub-core/fs/sfs.c @@ -429,6 +429,9 @@ grub_sfs_mount (grub_disk_t disk) - 24 /* offsetof (struct grub_sfs_objc, objects) */ - 25); /* offsetof (struct grub_sfs_obj, filename) */ data->label = grub_zalloc (max_len + 1); + if (data->label == NULL) + goto fail; + grub_strncpy (data->label, (char *) rootobjc->objects[0].filename, max_len); grub_free (rootobjc_data); From d13b6e8ebd10b4eb16698a002aa40258cf6e6f0e Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Wed, 29 Jan 2025 06:48:37 +0000 Subject: [PATCH 314/402] script/execute: Fix potential underflow and NULL dereference The result is initialized to 0 in grub_script_arglist_to_argv(). If the for loop condition is not met both result.args and result.argc remain 0 causing result.argc - 1 to underflow and/or result.args NULL dereference. Fix the issues by adding relevant checks. Fixes: CID 473880 Signed-off-by: Lidong Chen 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 e1450f45d..a86e0051f 100644 --- a/grub-core/script/execute.c +++ b/grub-core/script/execute.c @@ -760,6 +760,9 @@ cleanup: } } + if (result.args == NULL || result.argc == 0) + goto fail; + if (! result.args[result.argc - 1]) result.argc--; From 66733f7c7dae889861ea3ef3ec0710811486019e Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Wed, 29 Jan 2025 06:48:38 +0000 Subject: [PATCH 315/402] osdep/unix/getroot: Fix potential underflow The entry_len is initialized in grub_find_root_devices_from_mountinfo() to 0 before the while loop iterates through /proc/self/mountinfo. If the file is empty or contains only invalid entries entry_len remains 0 causing entry_len - 1 in the subsequent for loop initialization to underflow. To prevent this add a check to ensure entry_len > 0 before entering the for loop. Fixes: CID 473877 Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper Reviewed-by: Ross Philipson --- grub-core/osdep/linux/getroot.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/osdep/linux/getroot.c b/grub-core/osdep/linux/getroot.c index 7dd775d2a..527d4f0c5 100644 --- a/grub-core/osdep/linux/getroot.c +++ b/grub-core/osdep/linux/getroot.c @@ -484,6 +484,9 @@ again: } } + if (!entry_len) + goto out; + /* Now scan visible mounts for the ones we're interested in. */ for (i = entry_len - 1; i >= 0; i--) { From f8795cde217e21539c2f236bcbb1a4bf521086b3 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Tue, 21 Jan 2025 19:02:39 +0000 Subject: [PATCH 316/402] misc: Ensure consistent overflow error messages Update the overflow error messages to make them consistent across the GRUB code. Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper --- grub-core/fs/ntfs.c | 2 +- grub-core/fs/ntfscomp.c | 2 +- grub-core/video/readers/png.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index e00349b1d..960833a34 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -574,7 +574,7 @@ retry: goto retry; } } - return grub_error (GRUB_ERR_BAD_FS, "run list overflown"); + return grub_error (GRUB_ERR_BAD_FS, "run list overflow"); } ctx->curr_vcn = ctx->next_vcn; ctx->next_vcn += read_run_data (run, c1, 0); /* length of current VCN */ diff --git a/grub-core/fs/ntfscomp.c b/grub-core/fs/ntfscomp.c index f168a318e..b68bf5e40 100644 --- a/grub-core/fs/ntfscomp.c +++ b/grub-core/fs/ntfscomp.c @@ -30,7 +30,7 @@ static grub_err_t decomp_nextvcn (struct grub_ntfs_comp *cc) { if (cc->comp_head >= cc->comp_tail) - return grub_error (GRUB_ERR_BAD_FS, "compression block overflown"); + return grub_error (GRUB_ERR_BAD_FS, "compression block overflow"); if (grub_disk_read (cc->disk, (cc->comp_table[cc->comp_head].next_lcn - diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c index 3163e97bf..aa7524b7d 100644 --- a/grub-core/video/readers/png.c +++ b/grub-core/video/readers/png.c @@ -626,7 +626,7 @@ static grub_err_t grub_png_output_byte (struct grub_png_data *data, grub_uint8_t n) { if (--data->raw_bytes < 0) - return grub_error (GRUB_ERR_BAD_FILE_TYPE, "image size overflown"); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "image size overflow"); if (data->cur_column == 0) { From 9907d9c2723304b42cf6da74f1cc6c4601391956 Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Tue, 4 Feb 2025 15:11:10 +0000 Subject: [PATCH 317/402] bus/usb/ehci: Define GRUB_EHCI_TOGGLE as grub_uint32_t The Coverity indicates that GRUB_EHCI_TOGGLE is an int that contains a negative value and we are using it for the variable token which is grub_uint32_t. To remedy this we can cast the definition to grub_uint32_t. Fixes: CID 473851 Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- grub-core/bus/usb/ehci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/bus/usb/ehci.c b/grub-core/bus/usb/ehci.c index 9abebc6bd..2db07c7c0 100644 --- a/grub-core/bus/usb/ehci.c +++ b/grub-core/bus/usb/ehci.c @@ -218,7 +218,7 @@ enum #define GRUB_EHCI_TERMINATE (1<<0) -#define GRUB_EHCI_TOGGLE (1<<31) +#define GRUB_EHCI_TOGGLE ((grub_uint32_t) 1<<31) enum { From 5b36a5210e21bee2624f8acc36aefd8f10266adb Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Tue, 4 Feb 2025 15:11:11 +0000 Subject: [PATCH 318/402] normal/menu: Use safe math to avoid an integer overflow The Coverity indicates that the variable current_entry might overflow. To prevent this use safe math when adding GRUB_MENU_PAGE_SIZE to current_entry. On the occasion fix limiting condition which was broken. Fixes: CID 473853 Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- grub-core/normal/menu.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c index f24544b27..b946c834d 100644 --- a/grub-core/normal/menu.c +++ b/grub-core/normal/menu.c @@ -32,6 +32,7 @@ #include #include #include +#include /* Time to delay after displaying an error message about a default/fallback entry failing to boot. */ @@ -751,9 +752,7 @@ run_menu (grub_menu_t menu, int nested, int *auto_boot, int *notify_boot) case GRUB_TERM_CTRL | 'c': case GRUB_TERM_KEY_NPAGE: - if (current_entry + GRUB_MENU_PAGE_SIZE < menu->size) - current_entry += GRUB_MENU_PAGE_SIZE; - else + if (grub_add (current_entry, GRUB_MENU_PAGE_SIZE, ¤t_entry) || current_entry >= menu->size) current_entry = menu->size - 1; menu_set_chosen_entry (current_entry); break; From 8e6e87e7923ca2ae880021cb42a35cc9bb4c8fe2 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Thu, 6 Feb 2025 18:16:56 +0000 Subject: [PATCH 319/402] kern/partition: Add sanity check after grub_strtoul() call The current code incorrectly assumes that both the input and the values returned by grub_strtoul() are always valid which can lead to potential errors. This fix ensures proper validation to prevent any unintended issues. Fixes: CID 473843 Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper --- grub-core/kern/partition.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/grub-core/kern/partition.c b/grub-core/kern/partition.c index 704512a20..c6a578cf4 100644 --- a/grub-core/kern/partition.c +++ b/grub-core/kern/partition.c @@ -125,14 +125,22 @@ grub_partition_probe (struct grub_disk *disk, const char *str) for (ptr = str; *ptr;) { grub_partition_map_t partmap; - int num; + unsigned long num; const char *partname, *partname_end; partname = ptr; while (*ptr && grub_isalpha (*ptr)) ptr++; partname_end = ptr; - num = grub_strtoul (ptr, &ptr, 0) - 1; + + num = grub_strtoul (ptr, &ptr, 0); + if (*ptr != '\0' || num == 0 || num > GRUB_INT_MAX) + { + grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid partition number")); + return 0; + } + + num -= 1; curpart = 0; /* Use the first partition map type found. */ From a8d6b06331a75d75b46f3dd6cc6fcd40dcf604b7 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Thu, 6 Feb 2025 18:16:57 +0000 Subject: [PATCH 320/402] kern/misc: Add sanity check after grub_strtoul() call When the format string, fmt0, includes a positional argument grub_strtoul() or grub_strtoull() is called to extract the argument position. However, the returned argument position isn't fully validated. If the format is something like "%0$x" then these functions return 0 which leads to an underflow in the calculation of the args index, curn. The fix is to add a check to ensure the extracted argument position is greater than 0 before computing curn. Additionally, replace one grub_strtoull() with grub_strtoul() and change curn type to make code more correct. Fixes: CID 473841 Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper --- grub-core/kern/misc.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c index 7cee5d75c..2b7922393 100644 --- a/grub-core/kern/misc.c +++ b/grub-core/kern/misc.c @@ -830,7 +830,7 @@ parse_printf_arg_fmt (const char *fmt0, struct printf_args *args, while ((c = *fmt++) != 0) { int longfmt = 0; - grub_size_t curn; + unsigned long curn; const char *p; if (c != '%') @@ -848,7 +848,10 @@ parse_printf_arg_fmt (const char *fmt0, struct printf_args *args, if (*fmt == '$') { - curn = grub_strtoull (p, 0, 10) - 1; + curn = grub_strtoul (p, 0, 10); + if (curn == 0) + continue; + curn--; fmt++; } @@ -1034,6 +1037,8 @@ grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt0, if (*fmt == '$') { + if (format1 == 0) + continue; curn = format1 - 1; fmt++; format1 = 0; From 490a6ab71cebd96fae7a1ceb9067484f5ccbec2a Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Fri, 7 Feb 2025 01:47:57 +0000 Subject: [PATCH 321/402] loader/i386/linux: Cast left shift to grub_uint32_t The Coverity complains that we might overflow into a negative value when setting linux_params.kernel_alignment to (1 << align). We can remedy this by casting it to grub_uint32_t. Fixes: CID 473876 Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- grub-core/loader/i386/linux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index 977757f2c..b051600c8 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -806,7 +806,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), } linux_params.code32_start = prot_mode_target + lh.code32_start - GRUB_LINUX_BZIMAGE_ADDR; - linux_params.kernel_alignment = (1 << align); + linux_params.kernel_alignment = ((grub_uint32_t) 1 << align); linux_params.ps_mouse = linux_params.padding11 = 0; linux_params.type_of_loader = GRUB_LINUX_BOOT_LOADER_TYPE; From 4dc6166571645780c459dde2cdc1b001a5ec844c Mon Sep 17 00:00:00 2001 From: Alec Brown Date: Wed, 5 Feb 2025 22:04:08 +0000 Subject: [PATCH 322/402] loader/i386/bsd: Use safe math to avoid underflow The operation kern_end - kern_start may underflow when we input it into grub_relocator_alloc_chunk_addr() call. To avoid this we can use safe math for this subtraction. Fixes: CID 73845 Signed-off-by: Alec Brown Reviewed-by: Daniel Kiper --- grub-core/loader/i386/bsd.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/grub-core/loader/i386/bsd.c b/grub-core/loader/i386/bsd.c index 1f9128f6f..578433402 100644 --- a/grub-core/loader/i386/bsd.c +++ b/grub-core/loader/i386/bsd.c @@ -1340,6 +1340,7 @@ static grub_err_t grub_bsd_load_elf (grub_elf_t elf, const char *filename) { grub_err_t err; + grub_size_t sz; kern_end = 0; kern_start = ~0; @@ -1370,8 +1371,11 @@ grub_bsd_load_elf (grub_elf_t elf, const char *filename) if (grub_errno) return grub_errno; - err = grub_relocator_alloc_chunk_addr (relocator, &ch, - kern_start, kern_end - kern_start); + + if (grub_sub (kern_end, kern_start, &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "underflow detected while determining size of kernel for relocator"); + + err = grub_relocator_alloc_chunk_addr (relocator, &ch, kern_start, sz); if (err) return err; @@ -1431,8 +1435,10 @@ grub_bsd_load_elf (grub_elf_t elf, const char *filename) { grub_relocator_chunk_t ch; - err = grub_relocator_alloc_chunk_addr (relocator, &ch, kern_start, - kern_end - kern_start); + if (grub_sub (kern_end, kern_start, &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "underflow detected while determining size of kernel for relocator"); + + err = grub_relocator_alloc_chunk_addr (relocator, &ch, kern_start, sz); if (err) return err; kern_chunk_src = get_virtual_current_address (ch); From 13f005ed8308d31385ed1147c4fbd33ea870582f Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Sat, 2 Nov 2024 00:54:40 +0200 Subject: [PATCH 323/402] loader/i386/linux: Fix cleanup if kernel doesn't support 64-bit addressing Simply returning from grub_cmd_linux() doesn't free "file" resource nor calls grub_dl_ref(my_mod). Jump to "fail" label for proper cleanup like other error checks do. Signed-off-by: Sergii Dmytruk Reviewed-by: Daniel Kiper --- grub-core/loader/i386/linux.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index b051600c8..b7c1e057e 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -838,9 +838,10 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), #ifdef GRUB_MACHINE_EFI #ifdef __x86_64__ if (grub_le_to_cpu16 (linux_params.version) < 0x0208 && - ((grub_addr_t) grub_efi_system_table >> 32) != 0) - return grub_error(GRUB_ERR_BAD_OS, - "kernel does not support 64-bit addressing"); + ((grub_addr_t) grub_efi_system_table >> 32) != 0) { + grub_errno = grub_error (GRUB_ERR_BAD_OS, "kernel does not support 64-bit addressing"); + goto fail; + } #endif if (grub_le_to_cpu16 (linux_params.version) >= 0x0208) From f2a1f66e721d2231c3f7d80733c33cd808f0155d Mon Sep 17 00:00:00 2001 From: Duan Yayong Date: Thu, 28 Nov 2024 11:48:26 +0800 Subject: [PATCH 324/402] kern/i386/tsc_pmtimer: The GRUB menu gets stuck due to failed calibration The grub_divmod64() may return 0 but grub_tsc_calibrate_from_pmtimer() still returns 1 saying calibration succeeded. Of course it is not true. So, return 0 when grub_divmod64() returns 0. This way other calibration functions can be called subsequently. Signed-off-by: Duan Yayong Signed-off-by: Li Yongqiang Signed-off-by: Sun Ming Reviewed-by: Daniel Kiper --- grub-core/kern/i386/tsc_pmtimer.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/grub-core/kern/i386/tsc_pmtimer.c b/grub-core/kern/i386/tsc_pmtimer.c index 5c03c510a..dd044590d 100644 --- a/grub-core/kern/i386/tsc_pmtimer.c +++ b/grub-core/kern/i386/tsc_pmtimer.c @@ -143,5 +143,15 @@ grub_tsc_calibrate_from_pmtimer (void) if (tsc_diff == 0) return 0; grub_tsc_rate = grub_divmod64 ((1ULL << 32), tsc_diff, 0); + /* + * Specifically, when the tsc_diff (end_tsc - start_tsc) is greater than (1ULL << 32), + * the result of grub_divmod64() becomes zero, causing grub_tsc_rate to always be zero. + * As a result, grub_tsc_get_time_ms() consistently returns zero, and the GRUB menu + * countdown gets stuck. To resolve this, we return 0 to proceed to the next calibration + * function when grub_tsc_rate is zero. + */ + if (grub_tsc_rate == 0) + return 0; + return 1; } From 531750f7bfc475332b2046dd516bab582ba7decf Mon Sep 17 00:00:00 2001 From: Duan Yayong Date: Mon, 9 Dec 2024 14:48:32 +0800 Subject: [PATCH 325/402] i386/tsc: The GRUB menu gets stuck due to unserialized rdtsc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch is used to fix GRUB menu gets stuck in server AC poweron/poweroff stress test of x86_64, which is reproduced with 1/200 ratio. The root cause analysis as below: Q: What's the code logic? A: The grub_tsc_init() function will init tsc by setting grub_tsc_rate, which call stack is: grub_tsc_init() -> grub_tsc_calibrate_from_pmtimer() -> grub_divmod64() Among, grub_divmod64() function needs tsc_diff as the second parameter. In grub_pmtimer_wait_count_tsc(), we will call grub_get_tsc() function to get time stamp counter value to assign to start_tsc variable, and get into while (1) loop space to get end_tsc variable value with same function, after 3580 ticks, return "end_tsc - start_tsc". Actually, rdtsc instruction will be called in grub_get_tsc, but rdtsc instruction is not reliable (for the reason see the next question), which will cause tsc_diff to be a very big number larger than (1UL << 32) or a negative number, so that grub_tsc_rate will be zero. When run_menu() function is startup, and calls grub_tsc_get_time_ms() function to get current time to check if timeout time reach, at this time, grub_tsc_get_time_ms() function will return zero due to zero grub_tsc_rate variable, then GRUB menu gets stuck... Q: What's the difference between rdtsc and rdtscp instructions in x86_64 architecture? Here is more explanations from IntelÂź 64 and IA-32 Architectures Software Developer’s Manual Volume 2B (December 2024): https://cdrdv2.intel.com/v1/dl/getContent/671241 A: In page 4-558 -> RDTSC—Read Time-Stamp Counter: The RDTSC instruction is not a serializing instruction. It does not necessarily wait until all previous instructions have been executed before reading the counter. Similarly, subsequent instructions may begin execution before the read operation is performed. The following items may guide software seeking to order executions of RDTSC: - If software requires RDTSC to be executed only after all previous instructions have executed and all previous loads are globally visible, it can execute LFENCE immediately before RDTSC. - If software requires RDTSC to be executed only after all previous instructions have executed and all previous loads and stores are globally visible, it can execute the sequence MFENCE;LFENCE immediately before RDTSC. - If software requires RDTSC to be executed prior to execution of any subsequent instruction (including any memory accesses), it can execute the sequence LFENCE immediately after RDTSC. A: In page 4-560 -> RDTSCP—Read Time-Stamp Counter and Processor ID: The RDTSCP instruction is not a serializing instruction, but it does wait until all previous instructions have executed and all previous loads are globally visible. But it does not wait for previous stores to be globally visible, and subsequent instructions may begin execution before the read operation is performed. The following items may guide software seeking to order executions of RDTSCP: - If software requires RDTSCP to be executed only after all previous stores are globally visible, it can execute MFENCE immediately before RDTSCP. - If software requires RDTSCP to be executed prior to execution of any subsequent instruction (including any memory accesses), it can execute LFENCE immediately after RDTSCP. Q: Why there is a cpuid serializing instruction before rdtsc instruction, but "grub_get_tsc" still cannot work as expect? A: From IntelÂź 64 and IA-32 Architectures Software Developer's Manual Volume 2A: Instruction Set Reference, A-L (December 2024): https://cdrdv2.intel.com/v1/dl/getContent/671199 In page 3-222 -> CPUID—CPU Identification: CPUID can be executed at any privilege level to serialize instruction execution. Serializing instruction execution guarantees that any modifications to flags, registers, and memory for previous instructions are completed before the next instruction is fetched and executed. So we only kept the instruction rdtsc and its previous instruction in order currently. But it is still out-of-order possibility between rdtsc instruction and its subsequent instruction. Q: Why do we do this fix? A: In the one hand, add cpuid instruction after rdtsc instruction to make sure rdtsc instruction to be executed prior to execution of any subsequent instruction, about serializing execution that all previous instructions have been executed before rdtsc, there is a cpuid usage in original code. In the other hand, using cpuid instruction rather than lfence can make sure a forward compatibility for previous HW. Base this fix, we did 1500 cycles power on/off stress test, and did not reproduce this issue again. Fixes: https://savannah.gnu.org/bugs/?66257 Signed-off-by: Duan Yayong Signed-off-by: Li Yongqiang Signed-off-by: Sun Ming Reviewed-by: Daniel Kiper --- include/grub/i386/tsc.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/grub/i386/tsc.h b/include/grub/i386/tsc.h index 947e62fa1..3c96d3e0b 100644 --- a/include/grub/i386/tsc.h +++ b/include/grub/i386/tsc.h @@ -47,6 +47,11 @@ grub_get_tsc (void) /* Read TSC value. We cannot use "=A", since this would use %rax on x86_64. */ asm volatile ("rdtsc":"=a" (lo), "=d" (hi)); + /* + * Run cpuid after rdtsc instruction to ensure rdtsc is executed before + * subsequent instruction. + */ + grub_cpuid (0, a, b, c, d); return (((grub_uint64_t) hi) << 32) | lo; } From 224aefd0574fa9325e5e0682b6b96f77084798ce Mon Sep 17 00:00:00 2001 From: Ruihan Li Date: Mon, 16 Dec 2024 12:26:58 +0800 Subject: [PATCH 326/402] kern/efi/mm: Reset grub_mm_add_region_fn after ExitBootServices() call The EFI Boot Services can be used after ExitBootServices() call because the GRUB code still may allocate memory. An example call stack is: grub_multiboot_boot grub_multiboot2_make_mbi grub_efi_finish_boot_services b->exit_boot_services normal_boot grub_relocator32_boot grub_relocator_alloc_chunk_align_safe grub_relocator_alloc_chunk_align grub_malloc grub_memalign grub_mm_add_region_fn [= grub_efi_mm_add_regions] grub_efi_allocate_any_pages grub_efi_allocate_pages_real b->allocate_pages This can lead to confusing errors. After ExitBootServices() call b->allocate_pages may point to the NULL address resulting in something like: !!!! X64 Exception Type - 01(#DB - Debug) CPU Apic ID - 00000000 !!!! RIP - 000000000000201F, CS - 0000000000000038, RFLAGS - 0000000000200002 RAX - 000000007F9EE010, RCX - 0000000000000001, RDX - 0000000000000002 RBX - 0000000000000006, RSP - 00000000001CFBEC, RBP - 0000000000000000 RSI - 0000000000000000, RDI - 00000000FFFFFFFF R8 - 0000000000000006, R9 - 000000007FEDFFB8, R10 - 0000000000000000 R11 - 0000000000000475, R12 - 0000000000000001, R13 - 0000000000000002 R14 - 00000000FFFFFFFF, R15 - 000000007E432C08 DS - 0000000000000030, ES - 0000000000000030, FS - 0000000000000030 GS - 0000000000000030, SS - 0000000000000030 CR0 - 0000000080010033, CR2 - 0000000000000000, CR3 - 000000007FC01000 CR4 - 0000000000000668, CR8 - 0000000000000000 DR0 - 0000000000000000, DR1 - 0000000000000000, DR2 - 0000000000000000 DR3 - 0000000000000000, DR6 - 00000000FFFF0FF0, DR7 - 0000000000000400 GDTR - 000000007F9DE000 0000000000000047, LDTR - 0000000000000000 IDTR - 000000007F470018 0000000000000FFF, TR - 0000000000000000 FXSAVE_STATE - 00000000001CF840 Ideally we would like to avoid all memory allocations after exiting EFI Boot Services altogether but that requires significant code changes. This patch adds a simple workaround that resets grub_mm_add_region_fn to NULL after ExitBootServices() call, so: - Memory allocations have a better chance of succeeding because grub_memalign() will try to reclaim the disk cache if it sees a NULL in grub_mm_add_region_fn. - At worst it will fail to allocate memory but it will explicitly tell users that it's out of memory, which is still much better than the current situation where it fails in a fairly random way and triggers a CPU fault. Signed-off-by: Ruihan Li Reviewed-by: Daniel Kiper --- grub-core/kern/efi/mm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c index bc97ecd47..df238b165 100644 --- a/grub-core/kern/efi/mm.c +++ b/grub-core/kern/efi/mm.c @@ -297,6 +297,12 @@ grub_efi_finish_boot_services (grub_efi_uintn_t *outbuf_size, void *outbuf, if (efi_desc_version) *efi_desc_version = finish_desc_version; + /* + * We cannot request new memory regions from the EFI Boot Services anymore. + * FIXME: Can we completely avoid memory allocations after this? + */ + grub_mm_add_region_fn = NULL; + #if defined (__i386__) || defined (__x86_64__) if (is_apple) stop_broadcom (); From 11b9c2dd0d341a319209b8555db2d5d1f13fa152 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 16 Jan 2025 13:25:14 +0100 Subject: [PATCH 327/402] gdb_helper: Typo hueristic %s/hueristic/heuristic/ Signed-off-by: Heinrich Schuchardt Reviewed-by: Ross Philipson Reviewed-by: Daniel Kiper --- grub-core/gdb_helper.py.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/gdb_helper.py.in b/grub-core/gdb_helper.py.in index 5ed9eab0f..a2cab416a 100644 --- a/grub-core/gdb_helper.py.in +++ b/grub-core/gdb_helper.py.in @@ -10,7 +10,7 @@ gdb.prompt_hook = prompt_hook class IsGrubLoaded (gdb.Function): """Return 1 if GRUB has been loaded in memory, otherwise 0. -The hueristic used is checking if the first 4 bytes of the memory pointed +The heuristic used is checking if the first 4 bytes of the memory pointed to by the _start symbol are not 0. This is true for QEMU on the first run of GRUB. This may not be true on physical hardware, where memory is not necessarily cleared on soft reset. This may not also be true in QEMU on From 7161e2437dda654c69b930edb2fd18bbfe5c1f05 Mon Sep 17 00:00:00 2001 From: Lukas Fink Date: Sun, 5 Jan 2025 02:24:11 -0600 Subject: [PATCH 328/402] commands/file: Fix NULL dereference in the knetbsd tests The pointer returned by grub_elf_file() is not checked to verify it is not NULL before use. A NULL pointer may be returned when the given file does not have a valid ELF header. Fixes: https://savannah.gnu.org/bugs/?61960 Signed-off-by: Glenn Washburn Signed-off-by: Lukas Fink Reviewed-by: Ross Philipson Reviewed-by: Daniel Kiper --- grub-core/commands/file.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grub-core/commands/file.c b/grub-core/commands/file.c index 7c13e976b..19602d757 100644 --- a/grub-core/commands/file.c +++ b/grub-core/commands/file.c @@ -306,6 +306,8 @@ grub_cmd_file (grub_extcmd_context_t ctxt, int argc, char **args) elf = grub_elf_file (file, file->name); + if (elf == NULL) + break; if (elf->ehdr.ehdr32.e_type != grub_cpu_to_le16_compile_time (ET_EXEC) || elf->ehdr.ehdr32.e_ident[EI_DATA] != ELFDATA2LSB) break; From f0a08324d0f923527ba611887a3780c1f2cb1578 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 21 Jan 2025 11:01:26 -0600 Subject: [PATCH 329/402] term/ns8250-spcr: Return if redirection is disabled The Microsoft spec for SPCR says "The base address of the Serial Port register set described using the ACPI Generic Address Structure, or 0 if console redirection is disabled". So, return early if redirection is disabled (base address = 0). If this check is not done we may get invalid ports on machines with redirection disabled and boot may hang when reading the grub.cfg file. Signed-off-by: Benjamin Herrenschmidt Reviewed-by: Leo Sandoval Reviewed-by: Daniel Kiper --- grub-core/term/ns8250-spcr.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/grub-core/term/ns8250-spcr.c b/grub-core/term/ns8250-spcr.c index 4efaaf768..428b2d59a 100644 --- a/grub-core/term/ns8250-spcr.c +++ b/grub-core/term/ns8250-spcr.c @@ -76,6 +76,11 @@ grub_ns8250_spcr_init (void) config.speed = 115200; break; }; + + /* If base address is 0 it means redirection is disabled. */ + if (spcr->base_addr.addr == 0) + return NULL; + switch (spcr->base_addr.space_id) { case GRUB_ACPI_GENADDR_MEM_SPACE: From c730eddd2a820ab690875c25dd1032be3305bd0d Mon Sep 17 00:00:00 2001 From: Leo Sandoval Date: Wed, 22 Jan 2025 14:10:24 -0600 Subject: [PATCH 330/402] disk/ahci: Remove conditional operator for endtime The conditional makes no sense when the two possible expressions have the same value, so, remove it (perhaps the compiler does it for us but better to remove it). This change makes spinup argument unused. So, drop it as well. Signed-off-by: Leo Sandoval Reviewed-by: Daniel Kiper --- grub-core/disk/ahci.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/grub-core/disk/ahci.c b/grub-core/disk/ahci.c index e7b5dc5f2..f3c968195 100644 --- a/grub-core/disk/ahci.c +++ b/grub-core/disk/ahci.c @@ -152,8 +152,7 @@ struct grub_ahci_device static grub_err_t grub_ahci_readwrite_real (struct grub_ahci_device *dev, - struct grub_disk_ata_pass_through_parms *parms, - int spinup, int reset); + struct grub_disk_ata_pass_through_parms *parms, int reset); enum @@ -573,7 +572,7 @@ grub_ahci_pciinit (grub_pci_device_t dev, /* struct grub_disk_ata_pass_through_parms parms2; grub_memset (&parms2, 0, sizeof (parms2)); parms2.taskfile.cmd = 8; - grub_ahci_readwrite_real (dev, &parms2, 1, 1);*/ + grub_ahci_readwrite_real (dev, &parms2, 1);*/ } endtime = grub_get_time_ms () + 32000; @@ -908,15 +907,14 @@ grub_ahci_reset_port (struct grub_ahci_device *dev, int force) dev->hba->ports[dev->port].sata_error = dev->hba->ports[dev->port].sata_error; grub_memset (&parms2, 0, sizeof (parms2)); parms2.taskfile.cmd = 8; - return grub_ahci_readwrite_real (dev, &parms2, 1, 1); + return grub_ahci_readwrite_real (dev, &parms2, 1); } return GRUB_ERR_NONE; } static grub_err_t grub_ahci_readwrite_real (struct grub_ahci_device *dev, - struct grub_disk_ata_pass_through_parms *parms, - int spinup, int reset) + struct grub_disk_ata_pass_through_parms *parms, int reset) { struct grub_pci_dma_chunk *bufc; grub_uint64_t endtime; @@ -1038,7 +1036,7 @@ grub_ahci_readwrite_real (struct grub_ahci_device *dev, grub_dprintf ("ahci", "AHCI tfd = %x\n", dev->hba->ports[dev->port].task_file_data); - endtime = grub_get_time_ms () + (spinup ? 20000 : 20000); + endtime = grub_get_time_ms () + 20000; while ((dev->hba->ports[dev->port].command_issue & 1)) if (grub_get_time_ms () > endtime || (dev->hba->ports[dev->port].intstatus & GRUB_AHCI_HBA_PORT_IS_FATAL_MASK)) @@ -1097,9 +1095,9 @@ grub_ahci_readwrite_real (struct grub_ahci_device *dev, static grub_err_t grub_ahci_readwrite (grub_ata_t disk, struct grub_disk_ata_pass_through_parms *parms, - int spinup) + int spinup __attribute__((__unused__))) { - return grub_ahci_readwrite_real (disk->data, parms, spinup, 0); + return grub_ahci_readwrite_real (disk->data, parms, 0); } static grub_err_t From 348cd416a3574348f4255bf2b04ec95938990997 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Fri, 21 Feb 2025 09:06:12 +0800 Subject: [PATCH 331/402] fs/ext2: Rework out-of-bounds read for inline and external extents Previously, the number of extent entries was not properly capped based on the actual available space. This could lead to insufficient reads for external extents since the computation was based solely on the inline extent layout. In this patch, when processing the extent header we determine whether the header is stored inline, i.e. at inode->blocks.dir_blocks, or in an external extent block. We then clamp the number of entries accordingly (using max_inline_ext for inline extents and max_external_ext for external extent blocks). This change ensures that only the valid number of extent entries is processed preventing out-of-bound reads and potential filesystem corruption. Fixes: 7e2f750f0a (fs/ext2: Fix out-of-bounds read for inline extents) Signed-off-by: Michael Chang Tested-by: Christian Hesse Reviewed-by: Daniel Kiper --- grub-core/fs/ext2.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/grub-core/fs/ext2.c b/grub-core/fs/ext2.c index c3058f7e7..2f262dc34 100644 --- a/grub-core/fs/ext2.c +++ b/grub-core/fs/ext2.c @@ -496,7 +496,10 @@ grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) int i; grub_disk_addr_t ret; grub_uint16_t nent; + /* Maximum number of extent entries in the inode's inline extent area. */ const grub_uint16_t max_inline_ext = sizeof (inode->blocks) / sizeof (*ext) - 1; /* Minus 1 extent header. */ + /* Maximum number of extent entries in the external extent block. */ + const grub_uint16_t max_external_ext = EXT2_BLOCK_SIZE (data) / sizeof (*ext) - 1; /* Minus 1 extent header. */ if (grub_ext4_find_leaf (data, (struct grub_ext4_extent_header *) inode->blocks.dir_blocks, fileblock, &leaf) != GRUB_ERR_NONE) @@ -513,8 +516,18 @@ grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) nent = grub_le_to_cpu16 (leaf->entries); - if (leaf->depth == 0) + /* + * Determine the effective number of extent entries (nent) to process. + * If the extent header (leaf) is stored inline in the inode’s block + * area, i.e. at inode->blocks.dir_blocks, then only max_inline_ext + * entries can fit. Otherwise, if the header was read from an external + * extent block use the larger limit, max_external_ext, based on the + * full block size. + */ + if (leaf == (struct grub_ext4_extent_header *) inode->blocks.dir_blocks) nent = grub_min (nent, max_inline_ext); + else + nent = grub_min (nent, max_external_ext); for (i = 0; i < nent; i++) { From 1ed2628b560cedac7fd1a696985ab85b24541a8e Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 3 Feb 2025 15:41:22 -0800 Subject: [PATCH 332/402] fs/xfs: Add new superblock features added in Linux 6.12/6.13 The Linux port of XFS added a few new features in 2024. The existing GRUB driver doesn't attempt to read or write any of the new metadata, so, all three can be added to the incompat allowlist. On the occasion align XFS_SB_FEAT_INCOMPAT_NREXT64 value. Signed-off-by: Darrick J. Wong Reviewed-by: Daniel Kiper --- grub-core/fs/xfs.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index ab1281497..33e16bde5 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -88,7 +88,10 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define XFS_SB_FEAT_INCOMPAT_META_UUID (1 << 2) /* metadata UUID */ #define XFS_SB_FEAT_INCOMPAT_BIGTIME (1 << 3) /* large timestamps */ #define XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR (1 << 4) /* needs xfs_repair */ -#define XFS_SB_FEAT_INCOMPAT_NREXT64 (1 << 5) /* large extent counters */ +#define XFS_SB_FEAT_INCOMPAT_NREXT64 (1 << 5) /* large extent counters */ +#define XFS_SB_FEAT_INCOMPAT_EXCHRANGE (1 << 6) /* exchangerange supported */ +#define XFS_SB_FEAT_INCOMPAT_PARENT (1 << 7) /* parent pointers */ +#define XFS_SB_FEAT_INCOMPAT_METADIR (1 << 8) /* metadata dir tree */ /* * Directory entries with ftype are explicitly handled by GRUB code. @@ -98,6 +101,15 @@ GRUB_MOD_LICENSE ("GPLv3+"); * * We do not currently verify metadata UUID, so it is safe to read filesystems * with the XFS_SB_FEAT_INCOMPAT_META_UUID feature. + * + * We do not currently replay the log, so it is safe to read filesystems + * with the XFS_SB_FEAT_INCOMPAT_EXCHRANGE feature. + * + * We do not currently read directory parent pointers, so it is safe to read + * filesystems with the XFS_SB_FEAT_INCOMPAT_PARENT feature. + * + * We do not currently look at realtime or quota metadata, so it is safe to + * read filesystems with the XFS_SB_FEAT_INCOMPAT_METADIR feature. */ #define XFS_SB_FEAT_INCOMPAT_SUPPORTED \ (XFS_SB_FEAT_INCOMPAT_FTYPE | \ @@ -105,7 +117,10 @@ GRUB_MOD_LICENSE ("GPLv3+"); XFS_SB_FEAT_INCOMPAT_META_UUID | \ XFS_SB_FEAT_INCOMPAT_BIGTIME | \ XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR | \ - XFS_SB_FEAT_INCOMPAT_NREXT64) + XFS_SB_FEAT_INCOMPAT_NREXT64 | \ + XFS_SB_FEAT_INCOMPAT_EXCHRANGE | \ + XFS_SB_FEAT_INCOMPAT_PARENT | \ + XFS_SB_FEAT_INCOMPAT_METADIR) struct grub_xfs_sblock { From f209887381a56dea79152ab26ffb485718e3218e Mon Sep 17 00:00:00 2001 From: Egor Ignatov Date: Thu, 23 Jan 2025 20:44:14 +0300 Subject: [PATCH 333/402] fs/xfs: Fix grub_xfs_iterate_dir() return value in case of failure Commit ef7850c757 (fs/xfs: Fix issues found while fuzzing the XFS filesystem) introduced multiple boundary checks in grub_xfs_iterate_dir() but handled the error incorrectly returning error code instead of 0. Fix it. Also change the error message so that it doesn't match the message in grub_xfs_read_inode(). Fixes: ef7850c757 (fs/xfs: Fix issues found while fuzzing the XFS filesystem) Signed-off-by: Egor Ignatov Reviewed-by: Daniel Kiper --- grub-core/fs/xfs.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index 33e16bde5..cc0becbbe 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -885,7 +885,11 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, grub_uint8_t c; if ((inopos + (smallino ? 4 : 8)) > (grub_uint8_t *) dir + grub_xfs_fshelp_size (dir->data)) - return grub_error (GRUB_ERR_BAD_FS, "not a correct XFS inode"); + { + grub_error (GRUB_ERR_BAD_FS, "invalid XFS inode"); + return 0; + } + /* inopos might be unaligned. */ if (smallino) @@ -1004,7 +1008,10 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, filename = (char *)(direntry + 1); if (filename + direntry->len + 1 > (char *) end) - return grub_error (GRUB_ERR_BAD_FS, "invalid XFS directory entry"); + { + grub_error (GRUB_ERR_BAD_FS, "invalid XFS directory entry"); + return 0; + } /* The byte after the filename is for the filetype, padding, or tag, which is not used by GRUB. So it can be overwritten. */ From 3f440b5a53733027b118365f173e66b5c965bcdc Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Sat, 4 Jan 2025 01:41:15 -0600 Subject: [PATCH 334/402] docs: Replace @lbracechar{} and @rbracechar{} with @{ and @} Support for @lbracechar{} and @rbracechar{} was added in GNU Texinfo 5.0 but many older systems may have versions lower than this. Use @{ and @} to support a wider range of GNU Texinfo versions. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- docs/grub-dev.texi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi index 3ad8e3efa..f4367f895 100644 --- a/docs/grub-dev.texi +++ b/docs/grub-dev.texi @@ -614,10 +614,10 @@ tar xvzf libtasn1-4.19.0.tar.gz rm -rf grub-core/lib/libtasn1 mkdir -p grub-core/lib/libtasn1/lib mkdir -p grub-core/lib/libtasn1/tests -cp libtasn1-4.19.0/@lbracechar{}README.md,COPYING@rbracechar{} grub-core/lib/libtasn1 -cp libtasn1-4.19.0/lib/@lbracechar{}coding.c,decoding.c,element.c,element.h,errors.c,gstr.c,gstr.h,int.h,parser_aux.c,parser_aux.h,structure.c,structure.h@rbracechar{} grub-core/lib/libtasn1/lib +cp libtasn1-4.19.0/@{README.md,COPYING@} grub-core/lib/libtasn1 +cp libtasn1-4.19.0/lib/@{coding.c,decoding.c,element.c,element.h,errors.c,gstr.c,gstr.h,int.h,parser_aux.c,parser_aux.h,structure.c,structure.h@} grub-core/lib/libtasn1/lib cp libtasn1-4.19.0/lib/includes/libtasn1.h grub-core/lib/libtasn1 -cp libtasn1-4.19.0/tests/@lbracechar{}CVE-2018-1000654-1_asn1_tab.h,CVE-2018-1000654-2_asn1_tab.h,CVE-2018-1000654.c,object-id-decoding.c,object-id-encoding.c,octet-string.c,reproducers.c,Test_overflow.c,Test_simple.c,Test_strings.c@rbracechar{} grub-core/lib/libtasn1/tests +cp libtasn1-4.19.0/tests/@{CVE-2018-1000654-1_asn1_tab.h,CVE-2018-1000654-2_asn1_tab.h,CVE-2018-1000654.c,object-id-decoding.c,object-id-encoding.c,octet-string.c,reproducers.c,Test_overflow.c,Test_simple.c,Test_strings.c@} grub-core/lib/libtasn1/tests rm -rf libtasn1-4.19.0* @end example From 01f064064c3146bd82e0c8db0da11d286bca31c6 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Sat, 4 Jan 2025 01:50:32 -0600 Subject: [PATCH 335/402] docs: Do not reference non-existent --dumb option This appears to be a relic from GRUB legacy that used a --dumb option for its terminal command. The proper way to do this in GRUB2 is to set the terminal to "dumb" via the terminfo command. Fixes: https://savannah.gnu.org/bugs/?66302 Reported-by: Jernej Jakob Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- docs/grub.texi | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index e914e022b..dcc563dbc 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -2731,11 +2731,11 @@ command. However, note that GRUB assumes that your terminal emulator is compatible with VT100 by default. This is true for most terminal -emulators nowadays, but you should pass the option @option{--dumb} to -the command if your terminal emulator is not VT100-compatible or -implements few VT100 escape sequences. If you specify this option then -GRUB provides you with an alternative menu interface, because the normal -menu requires several fancy features of your terminal. +emulators nowadays. However if your terminal emulator is not VT100-compatible +or implements few VT100 escape sequences, you shoud tell GRUB that the +terminal is dumb using the @command{terminfo} (@pxref{terminfo}) command. +This will have GRUB provide you with an alternative menu interface, because +the normal menu requires several fancy features of your terminal. @node Vendor power-on keys From 56ccc5ed569869fd735074ccebeaa7aab058342d Mon Sep 17 00:00:00 2001 From: Pascal Hambourg Date: Fri, 21 Feb 2025 12:20:28 +0100 Subject: [PATCH 336/402] util/grub.d/30_os-prober.in: Fix GRUB_OS_PROBER_SKIP_LIST for non-EFI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GRUB documentation states: GRUB_OS_PROBER_SKIP_LIST List of space-separated FS UUIDs of filesystems to be ignored from os-prober output. For efi chainloaders it’s @ But the actual behaviour does not match this description. GRUB_OS_PROBER_SKIP_LIST="" does nothing. In order to skip non-EFI bootloaders, you must set GRUB_OS_PROBER_SKIP_LIST="@" which is both absurd, and are redundant, and wrong, such as /dev/sd* may not be persistent across boots. Also, any non-word character is accepted as a separator, including "-" and "@" which may be present in UUIDs. This can cause false positives because of partial UUID match. This patch fixes these flaws while retaining some backward compatibility with previous behaviour which may be expected by existing setups: - also accept @/dev/* (with warning) for non-EFI bootloaders, - also accept comma and semicolon as separator. Fixes: 55e706c9 (Add GRUB_OS_PROBER_SKIP_LIST to selectively skipping systems) Signed-off-by: Pascal Hambourg Reviewed-by: Daniel Kiper --- docs/grub.texi | 7 +++++-- util/grub.d/30_os-prober.in | 19 +++++++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index dcc563dbc..e96f1579a 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -1562,8 +1562,11 @@ this option to @samp{false} to enable this feature in the @command{grub-mkconfig} command. @item GRUB_OS_PROBER_SKIP_LIST -List of space-separated FS UUIDs of filesystems to be ignored from os-prober -output. For efi chainloaders it's @@ +List of space-separated case insensitive UUIDs of filesystems to be ignored +from os-prober output. For EFI chainloaders it's @@. For +backward compatibility with previous behaviour, @@/dev/* is also accepted +for non-EFI chainloaders even if the device does not match, and comma and +semicolon are also accepted as separator. @item GRUB_DISABLE_SUBMENU Normally, @command{grub-mkconfig} will generate top level menu entry for diff --git a/util/grub.d/30_os-prober.in b/util/grub.d/30_os-prober.in index 656301eaf..394747759 100644 --- a/util/grub.d/30_os-prober.in +++ b/util/grub.d/30_os-prober.in @@ -117,19 +117,26 @@ if [ "x$GRUB_TOP_LEVEL_OS_PROBER" != x ]; then OSPROBED=$(grub_move_to_front "$GRUB_TOP_LEVEL_OS_PROBER" ${OSPROBED}) fi +case "$GRUB_OS_PROBER_SKIP_LIST" in *@/[dD][eE][vV]/*) + grub_warn "$(gettext_printf "GRUB_OS_PROBER_SKIP_LIST contains deprecated @/dev/* notation. The @/dev/* suffix is ignored.")" +esac + for OS in ${OSPROBED} ; do DEVICE="`echo ${OS} | cut -d ':' -f 1`" LONGNAME="`echo ${OS} | cut -d ':' -f 2 | tr '^' ' '`" LABEL="`echo ${OS} | cut -d ':' -f 3 | tr '^' ' '`" BOOT="`echo ${OS} | cut -d ':' -f 4`" - if UUID="`${grub_probe} --target=fs_uuid --device ${DEVICE%@*}`"; then - EXPUUID="$UUID" + unset UUID + if [ -n "${GRUB_OS_PROBER_SKIP_LIST}" ] && UUID="`${grub_probe} --target=fs_uuid --device ${DEVICE%@*}`"; then + SPACE='[[:space:],;]' # regex matching spaces and common separators - if [ x"${DEVICE#*@}" != x ] ; then - EXPUUID="${EXPUUID}@${DEVICE#*@}" + if [ x"${DEVICE##*@*}" = x ] ; then + EXPUUID="$UUID@${DEVICE#*@}" + else + EXPUUID="$UUID(@/dev/.*)?" fi - if [ "x${GRUB_OS_PROBER_SKIP_LIST}" != "x" ] && [ "x`echo ${GRUB_OS_PROBER_SKIP_LIST} | grep -i -e '\b'${EXPUUID}'\b'`" != "x" ] ; then + if printf %s " ${GRUB_OS_PROBER_SKIP_LIST} " | grep -Eqie "${SPACE}${EXPUUID}${SPACE}" ; then echo "Skipped ${LONGNAME} on ${DEVICE} by user request." >&2 continue fi @@ -299,7 +306,7 @@ EOF echo "$title_correction_code" ;; macosx) - if [ "${UUID}" ]; then + if [ "${UUID=`${grub_probe} --target=fs_uuid --device ${DEVICE}`}" ]; then OSXUUID="${UUID}" osx_entry xnu_kernel 32 osx_entry xnu_kernel64 64 From fbcc3889178a98d6189da5ef0ed1146613cce750 Mon Sep 17 00:00:00 2001 From: Pascal Hambourg Date: Mon, 24 Feb 2025 18:10:45 +0100 Subject: [PATCH 337/402] util/grub.d/30_os-prober.in: Conditionally show or hide chain and efi menu entries On systems which support multiple boot platforms such as BIOS and EFI, it makes no sense to show menu entries which are not supported by the current boot platform. Menu entries generated from os-prober "chain" boot type use boot sector chainloading which is supported on PC BIOS platform only. Show "chain" menu entries only if boot platform is PC BIOS. Show "efi" menu entries only if boot platform is EFI. This is aimed to allow os-prober to report both EFI and PC BIOS boot loaders regardless of the current boot mode on x86 systems which support both EFI and legacy BIOS boot, in order to generate a config file which can be used with either BIOS or EFI boot. Signed-off-by: Pascal Hambourg Reviewed-by: Daniel Kiper --- util/grub.d/30_os-prober.in | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/util/grub.d/30_os-prober.in b/util/grub.d/30_os-prober.in index 394747759..5d8d75292 100644 --- a/util/grub.d/30_os-prober.in +++ b/util/grub.d/30_os-prober.in @@ -162,6 +162,8 @@ for OS in ${OSPROBED} ; do onstr="$(gettext_printf "(on %s)" "${DEVICE}")" cat << EOF +# This menu entry is supported only on PC BIOS platforms. +if [ "\$grub_platform" = "pc" ]; then menuentry '$(echo "${LONGNAME} $onstr" | grub_quote)' $CLASS --class os \$menuentry_id_option 'osprober-chain-$(grub_get_device_id "${DEVICE}")' { EOF save_default_entry | grub_add_tab @@ -186,6 +188,7 @@ EOF cat < Date: Fri, 10 Jan 2025 14:19:41 -0600 Subject: [PATCH 338/402] gitignore: Ignore generated files from libtasn The commit 504058e8 (libtasn1: Compile into asn1 module) generates files into the grub-core/lib/libtasn1-grub directory and commit 99cda678 (asn1_test: Test module for libtasn1) generates files into the grub-core/tests/asn1/tests directory. Ignore these directories as they are not under revision control. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 2105d87c8..524f2e6d0 100644 --- a/.gitignore +++ b/.gitignore @@ -141,10 +141,12 @@ widthspec.bin /grub-core/kernel.img.bin /grub-core/lib/gnulib /grub-core/lib/libgcrypt-grub +/grub-core/lib/libtasn1-grub /grub-core/modinfo.sh /grub-core/rs_decoder.h /grub-core/symlist.c /grub-core/symlist.h +/grub-core/tests/asn1/tests /grub-core/trigtables.c /grub-core/unidata.c /grub-editenv From 187338f1ac5e6f2a567f0799e5f291c48771d335 Mon Sep 17 00:00:00 2001 From: James Le Cuirot Date: Mon, 30 Dec 2024 10:52:23 +0000 Subject: [PATCH 339/402] script/execute: Don't let trailing blank lines determine the return code The grub_script_execute_sourcecode() parses and executes code one line at a time, updating the return code each time because only the last line determines the final status. However, trailing new lines were also executed, masking any failure on the previous line. Fix this by only trying to execute the command when there is actually one present. This has presumably never been noticed because this code is not used by regular functions, only in special cases like eval and menu entries. The latter generally don't return at all, having booted an OS. When failing to boot, upstream GRUB triggers the fallback mechanism regardless of the return code. We noticed the problem while using Red Hat's patches, which change this behaviour to take account of the return code. In that case, a failure takes you back to the menu rather than triggering a fallback. Signed-off-by: James Le Cuirot Reviewed-by: Daniel Kiper --- grub-core/script/execute.c | 5 ++++- tests/grub_script_eval.in | 10 +++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/grub-core/script/execute.c b/grub-core/script/execute.c index a86e0051f..da99dfa05 100644 --- a/grub-core/script/execute.c +++ b/grub-core/script/execute.c @@ -914,7 +914,10 @@ grub_script_execute_sourcecode (const char *source) break; } - ret = grub_script_execute (parsed_script); + /* Don't let trailing blank lines determine the return code. */ + if (parsed_script->cmd) + ret = grub_script_execute (parsed_script); + grub_script_free (parsed_script); grub_free (line); } diff --git a/tests/grub_script_eval.in b/tests/grub_script_eval.in index c97b78d77..9c6211042 100644 --- a/tests/grub_script_eval.in +++ b/tests/grub_script_eval.in @@ -3,4 +3,12 @@ eval echo "Hello world" valname=tst eval $valname=hi -echo $tst \ No newline at end of file +echo $tst + +if eval " +false +"; then + echo should have failed +else + echo failed as expected +fi From 696e35b7ff92689add3ba898bee4f4892b3a3cfe Mon Sep 17 00:00:00 2001 From: Yuri Zaporozhets Date: Mon, 23 Dec 2024 21:47:18 +0100 Subject: [PATCH 340/402] include/grub/mm.h: Remove duplicate inclusion of grub/err.h The header is included twice. Fix that. Signed-off-by: Yuri Zaporozhets Reviewed-by: Daniel Kiper --- include/grub/mm.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/grub/mm.h b/include/grub/mm.h index e1336d1c2..51ec0b8f9 100644 --- a/include/grub/mm.h +++ b/include/grub/mm.h @@ -23,7 +23,6 @@ #include #include #include -#include #include #ifndef NULL From cb639acea0174b7794c05e6d8833748818d78c57 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Sun, 2 Mar 2025 10:54:04 -0600 Subject: [PATCH 341/402] lib/tss2/tss2_structs.h: Fix clang build - remove duplicate typedef grub-core/lib/tss2/tss2_structs.h contains a duplicate typedef as follows: typedef TPMS_SCHEME_HASH_t TPMS_SCHEME_KDF2_t; This causes a build failure when compiling with clang. Remove the duplicate typedef which allows successfully building GRUB with clang. Signed-off-by: Andrew Hamilton Reviewed-by: Ross Philipson Reviewed-by: Gary Lin Reviewed-by: Daniel Kiper --- grub-core/lib/tss2/tss2_structs.h | 1 - 1 file changed, 1 deletion(-) diff --git a/grub-core/lib/tss2/tss2_structs.h b/grub-core/lib/tss2/tss2_structs.h index e5390ab56..2eefba87c 100644 --- a/grub-core/lib/tss2/tss2_structs.h +++ b/grub-core/lib/tss2/tss2_structs.h @@ -275,7 +275,6 @@ typedef TPMS_SCHEME_HASH_t TPMS_ENC_SCHEME_OAEP_t; typedef TPMS_SCHEME_HASH_t TPMS_SCHEME_KDF2_t; typedef TPMS_SCHEME_HASH_t TPMS_SCHEME_MGF1_t; typedef TPMS_SCHEME_HASH_t TPMS_SCHEME_KDF1_SP800_56A_t; -typedef TPMS_SCHEME_HASH_t TPMS_SCHEME_KDF2_t; typedef TPMS_SCHEME_HASH_t TPMS_SCHEME_KDF1_SP800_108_t; /* TPMS_SCHEME_HMAC Type */ From ee27f07a650d13f66ba7b867de3b39ddeb3631ec Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Mon, 3 Mar 2025 00:02:21 +0300 Subject: [PATCH 342/402] kern/partition: Unbreak support for nested partitions When using syntax "hd0,gtp3,dfly1" then ptr points to trailing part, ",dfly1". So, it's improper to consider it as an invalid partition. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/kern/partition.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/kern/partition.c b/grub-core/kern/partition.c index c6a578cf4..3b128e6d2 100644 --- a/grub-core/kern/partition.c +++ b/grub-core/kern/partition.c @@ -134,7 +134,7 @@ grub_partition_probe (struct grub_disk *disk, const char *str) partname_end = ptr; num = grub_strtoul (ptr, &ptr, 0); - if (*ptr != '\0' || num == 0 || num > GRUB_INT_MAX) + if (num == 0 || num > GRUB_INT_MAX) { grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid partition number")); return 0; From be0ae9583e962e1cb9b5bc7bd3513ede32cc95f1 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Tue, 4 Mar 2025 21:46:30 +0300 Subject: [PATCH 343/402] loader/i386/bsd: Fix type passed for the kernel FreeBSD loader always passes "elf kernel". We currently pass "elf64 kernel" when loading 64-bit kernel. The -CURRENT, HEAD, kernel accepts only "elf kernel". Older kernel accepts either. Tested with FreeBSD and DragonFlyBSD. Reference: https://cgit.freebsd.org/src/commit/?id=b72ae900d4348118829fe04abdc11b620930c30f Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/loader/i386/bsd.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/grub-core/loader/i386/bsd.c b/grub-core/loader/i386/bsd.c index 578433402..4b7c436d4 100644 --- a/grub-core/loader/i386/bsd.c +++ b/grub-core/loader/i386/bsd.c @@ -1543,9 +1543,7 @@ grub_cmd_freebsd (grub_extcmd_context_t ctxt, int argc, char *argv[]) grub_file_t file; int len = is_64bit ? 8 : 4; - err = grub_freebsd_add_meta_module (argv[0], is_64bit - ? FREEBSD_MODTYPE_KERNEL64 - : FREEBSD_MODTYPE_KERNEL, + err = grub_freebsd_add_meta_module (argv[0], FREEBSD_MODTYPE_KERNEL, argc - 1, argv + 1, kern_start, kern_end - kern_start); From 6a168afd32b2d66aacf784aec447826c398a2ad4 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Fri, 28 Feb 2025 15:55:23 -0600 Subject: [PATCH 344/402] docs: Document restricted filesystems in lockdown Document which filesystems are not allowed when lockdown is enabled to align to recent GRUB changes. Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- docs/grub.texi | 89 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index e96f1579a..23eb3ad81 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -363,6 +363,8 @@ Fast FileSystem (AFFS)}, @dfn{AtheOS fs}, @dfn{BeFS}, @dfn{BSD UFS/UFS2}, @dfn{XFS}, and @dfn{ZFS} (including lzjb, gzip, zle, mirror, stripe, raidz1/2/3 and encryption in AES-CCM and AES-GCM). @xref{Filesystem}, for more information. +Note: Only a subset of filesystems are supported in lockdown mode (such +as when secure boot is enabled, @pxref{Lockdown} for more information). @item Support automatic decompression Can decompress files which were compressed by @command{gzip} or @@ -843,6 +845,8 @@ not use any additional partition maps to access @file{/boot} F2FS, HFS, uncompressed HFS+, ISO9660, JFS, Minix, Minix2, Minix3, NILFS2, NTFS, ReiserFS, ROMFS, SFS, tar, UDF, UFS1, UFS2, XFS @end itemize +Note: Only a subset of filesystems are supported in lockdown mode (such +as when secure boot is enabled, @pxref{Lockdown} for more information). MBR gap has few technical problems. There is no way to reserve space in the embedding area with complete safety, and some proprietary software is @@ -4198,10 +4202,14 @@ This is used as part of LZO decompression / compression. @node affs_module @section affs This module provides support for the Amiga Fast FileSystem (AFFS). +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. @node afs_module @section afs This module provides support for the AtheOS File System (AFS). +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. @node afsplitter_module @section afsplitter @@ -4253,6 +4261,8 @@ to the terminal for the current call stack. @node bfs_module @section bfs This module provides support for the BeOS "Be File System" (BFS). +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. @node biosdisk_module @section biosdisk @@ -4342,6 +4352,8 @@ content of a file to the terminal. Please @pxref{cat} for more info. @section cbfs This module provides support for the Coreboot File System (CBFS) which is an archive based file system. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. @node cbls_module @section cbls @@ -4847,6 +4859,8 @@ contents of a file in hexadecimal. @xref{hexdump} for more information. @section hfs This module provides support for the Hierarchical File System (HFS) file system in GRUB. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. @node hfsplus_module @section hfsplus @@ -4887,6 +4901,8 @@ longer names) @node jfs_module @section jfs This module provides support for the Journaled File System (JFS) file system. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. @node jpeg_module @section jpeg @@ -5125,26 +5141,38 @@ modules. @node minix_module @section minix This module provides support for the Minix filesystem, version 1. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. @node minix2_module @section minix2 This module provides support for the Minix filesystem, version 2. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. @node minix2_be_module @section minix2_be This module provides support for the Minix filesystem, version 2 big-endian. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. @node minix3_module @section minix3 This module provides support for the Minix filesystem, version 3. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. @node minix3_be_module @section minix3_be This module provides support for the Minix filesystem, version 3 big-endian. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. @node minix_be_module @section minix_be This module provides support for the Minix filesystem, version 1 big-endian. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. @node mmap_module @section mmap @@ -5278,6 +5306,8 @@ something like "ASCII cpio archive (SVR4 with CRC)" @section nilfs2 This module provides support for the New Implementation of Log filesystem (nilfs2). +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. @node normal_module @section normal @@ -5287,11 +5317,15 @@ more information. @node ntfs_module @section ntfs This module provides support for the New Technology File System (NTFS) in GRUB. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. @node ntfscomp_module @section ntfscomp This module provides support for compression with the New Technology File System (NTFS) in GRUB. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. @node ntldr_module @section ntldr @@ -5517,6 +5551,8 @@ GRUB script wildcard translator. @xref{regexp} for more information. @node reiserfs_module @section reiserfs This module provides support for the ReiserFS File System in GRUB. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. @node relocator_module @section relocator @@ -5526,6 +5562,8 @@ to the expected memory location(s) and jumping to (invoking) the executable. @node romfs_module @section romfs 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 scsi_module @section scsi @@ -5594,6 +5632,8 @@ values from / to specified PCI / PCIe devices. @node sfs_module @section sfs This module provides support for the Amiga Smart File System (SFS) in GRUB. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. @node shift_test_module @section shift_test @@ -5742,19 +5782,27 @@ information provided by a U-Boot bootloader. @section udf This module provides support for the Universal Disk Format (UDF) used on some newer optical disks. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. @node ufs1_module @section ufs1 This module provides support for the Unix File System version 1 in GRUB. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. @node ufs1_be_module @section ufs1_be This module provides support for the Unix File System version 1 (big-endian) in GRUB. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. @node ufs2_module @section ufs2 This module provides support for the Unix File System version 2 in GRUB. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. @node uhci_module @section uhci @@ -8813,10 +8861,47 @@ platforms. 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 -be restricted and some operations/commands cannot be executed. +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. + +The filesystems currently allowed in lockdown mode include: +@itemize @bullet +@item BtrFS +@item cpio +@item exFAT +@item Enhanced Read-Only File System (EROFS) +@item Linux ext2/ext3/ext4 +@item F2FS +@item DOS FAT12/FAT16/FAT32 +@item HFS+ +@item ISO9660 +@item Squash4 +@item tar +@item XFS +@item ZFS +@end itemize + +The filesystems currently not allowed in lockdown mode include: +@itemize @bullet +@item Amiga Fast FileSystem (AFFS) +@item AtheOS File System (AFS) +@item Bee File System (BFS) +@item Coreboot File System (CBFS) +@item Hierarchical File System (HFS) +@item Journaled File System (JFS) +@item Minix filesystem +@item New Implementation of Log filesystem (nilfs2) +@item Windows New Technology File System (NTFS) +@item ReiserFS +@item Read-Only Memory File System (ROMFS) +@item Amiga Smart File System (SFS) +@item Universal Disk Format (UDF) +@item Unix File System (UFS) +@end itemize The @samp{lockdown} variable is set to @samp{y} when the GRUB is locked down. -Otherwise it does not exit. +Otherwise it does not exist. @node TPM2 key protector @section TPM2 key protector in GRUB From 1acf11fe4c38b43824c0e32e869b721fb2305332 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Fri, 28 Feb 2025 15:55:24 -0600 Subject: [PATCH 345/402] docs: Capture additional commands restricted by lockdown Update documentation to capture that all memrw commands, the minicmd dump command, and raw memory dumping via hexdump are restricted when lockdown is enabled. This aligns to recent GRUB code updates. Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- docs/grub.texi | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/grub.texi b/docs/grub.texi index 23eb3ad81..d9b26fa36 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -5124,6 +5124,8 @@ This module provides support for commands @command{read_byte}, write data to physical memory (addresses). The "read" commands accept one parameter to specify the source address. The "write" commands require either two or three parameters, with the order: address, value, . +Note: The commands provided by this module are not allowed when lockdown is +enforced (@pxref{Lockdown}). @node memtools_module @section memtools @@ -5137,6 +5139,8 @@ including: @command{cat}, @command{help}, @command{dump}, @command{rmmod}, @command{lsmod}, and @command{exit}. The version of the commands in this module are similar to their full-fledged counterparts implemented in other GRUB modules. +Note: The @command{dump} command is not allowed when lockdown is enforced +(@pxref{Lockdown}). @node minix_module @section minix @@ -7196,6 +7200,11 @@ to be shown. If given the special device named @samp{(mem)}, then the @samp{offset} given to @option{--skip} is treated as the address of a memory location to dump from. + +Note: The dumping of RAM memory (by the (mem) argument) is not allowed when +when lockdown is enforced (@pxref{Lockdown}). The dumping of disk or file +data is allowed when lockdown is enforced. + @end deffn From da9740cd520aa1d664d649ada6871c2eeef54611 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Sat, 1 Mar 2025 02:15:07 -0600 Subject: [PATCH 346/402] commands/acpi: Use options enum to index command options Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- grub-core/commands/acpi.c | 46 +++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/grub-core/commands/acpi.c b/grub-core/commands/acpi.c index 77be99a58..f72a19c03 100644 --- a/grub-core/commands/acpi.c +++ b/grub-core/commands/acpi.c @@ -38,6 +38,20 @@ GRUB_MOD_LICENSE ("GPLv3+"); +enum + { + OPTION_EXCLUDE = 0, + OPTION_LOAD_ONLY, + OPTION_V1, + OPTION_V2, + OPTION_OEMID, + OPTION_OEMTABLE, + OPTION_OEMTABLEREV, + OPTION_OEMTABLECREATOR, + OPTION_OEMTABLECREATORREV, + OPTION_NO_EBDA + }; + static const struct grub_arg_option options[] = { {"exclude", 'x', 0, N_("Don't load host tables specified by comma-separated list."), @@ -497,14 +511,14 @@ grub_cmd_acpi (struct grub_extcmd_context *ctxt, int argc, char **args) grub_size_t tbl_addr_size; struct grub_acpi_table_header *table_head; - exclude = state[0].set ? grub_strdup (state[0].arg) : 0; + exclude = state[OPTION_EXCLUDE].set ? grub_strdup (state[OPTION_EXCLUDE].arg) : 0; if (exclude) { for (ptr = exclude; *ptr; ptr++) *ptr = grub_tolower (*ptr); } - load_only = state[1].set ? grub_strdup (state[1].arg) : 0; + load_only = state[OPTION_LOAD_ONLY].set ? grub_strdup (state[OPTION_LOAD_ONLY].arg) : 0; if (load_only) { for (ptr = load_only; *ptr; ptr++) @@ -623,26 +637,26 @@ grub_cmd_acpi (struct grub_extcmd_context *ctxt, int argc, char **args) } /* Does user specify versions to generate? */ - if (state[2].set || state[3].set) + if (state[OPTION_V1].set || state[OPTION_V2].set) { - rev1 = state[2].set; - if (state[3].set) + rev1 = state[OPTION_V1].set; + if (state[OPTION_V2].set) rev2 = rev2 ? : 2; else rev2 = 0; } /* Does user override root header information? */ - if (state[4].set) - grub_strncpy (root_oemid, state[4].arg, sizeof (root_oemid)); - if (state[5].set) - grub_strncpy (root_oemtable, state[5].arg, sizeof (root_oemtable)); - if (state[6].set) - root_oemrev = grub_strtoul (state[6].arg, 0, 0); - if (state[7].set) - grub_strncpy (root_creator_id, state[7].arg, sizeof (root_creator_id)); - if (state[8].set) - root_creator_rev = grub_strtoul (state[8].arg, 0, 0); + if (state[OPTION_OEMID].set) + grub_strncpy (root_oemid, state[OPTION_OEMID].arg, sizeof (root_oemid)); + if (state[OPTION_OEMTABLE].set) + grub_strncpy (root_oemtable, state[OPTION_OEMTABLE].arg, sizeof (root_oemtable)); + if (state[OPTION_OEMTABLEREV].set) + root_oemrev = grub_strtoul (state[OPTION_OEMTABLEREV].arg, 0, 0); + if (state[OPTION_OEMTABLECREATOR].set) + grub_strncpy (root_creator_id, state[OPTION_OEMTABLECREATOR].arg, sizeof (root_creator_id)); + if (state[OPTION_OEMTABLECREATORREV].set) + root_creator_rev = grub_strtoul (state[OPTION_OEMTABLECREATORREV].arg, 0, 0); /* Load user tables */ for (i = 0; i < argc; i++) @@ -758,7 +772,7 @@ grub_cmd_acpi (struct grub_extcmd_context *ctxt, int argc, char **args) acpi_tables = 0; #if defined (__i386__) || defined (__x86_64__) - if (! state[9].set) + if (! state[OPTION_NO_EBDA].set) { grub_err_t err; err = grub_acpi_create_ebda (); From 112d2069cf348d424ab49e7cb01abdb2320ecee8 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Sat, 1 Mar 2025 23:15:32 -0600 Subject: [PATCH 347/402] commands/ls: Return proper GRUB_ERR_* for functions returning type grub_err_t Also, remove unused code. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- grub-core/commands/ls.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/grub-core/commands/ls.c b/grub-core/commands/ls.c index f660946a2..65b17768b 100644 --- a/grub-core/commands/ls.c +++ b/grub-core/commands/ls.c @@ -250,7 +250,6 @@ grub_ls_list_files (char *dirname, int longlist, int all, int human) if (ctx.dirname == NULL) goto fail; - all = 1; grub_memset (&info, 0, sizeof (info)); if (longlist) print_files_long (p, &info, &ctx); @@ -272,7 +271,7 @@ grub_ls_list_files (char *dirname, int longlist, int all, int human) grub_free (device_name); - return 0; + return GRUB_ERR_NONE; } static grub_err_t @@ -288,7 +287,7 @@ grub_cmd_ls (grub_extcmd_context_t ctxt, int argc, char **args) grub_ls_list_files (args[i], state[0].set, state[2].set, state[1].set); - return 0; + return GRUB_ERR_NONE; } static grub_extcmd_t cmd; From cbfb031b145f89523aa47d5f493669d6e0349149 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Sat, 1 Mar 2025 23:15:33 -0600 Subject: [PATCH 348/402] commands/ls: Merge print_files_long() and print_files() into print_file() Simplify the code by removing logic around which file printer to call. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- grub-core/commands/ls.c | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/grub-core/commands/ls.c b/grub-core/commands/ls.c index 65b17768b..2eaefaf90 100644 --- a/grub-core/commands/ls.c +++ b/grub-core/commands/ls.c @@ -89,24 +89,12 @@ struct grub_ls_list_files_ctx char *dirname; int all; int human; + int longlist; }; /* Helper for grub_ls_list_files. */ static int -print_files (const char *filename, const struct grub_dirhook_info *info, - void *data) -{ - struct grub_ls_list_files_ctx *ctx = data; - - if (ctx->all || filename[0] != '.') - grub_printf ("%s%s ", filename, info->dir ? "/" : ""); - - return 0; -} - -/* Helper for grub_ls_list_files. */ -static int -print_files_long (const char *filename, const struct grub_dirhook_info *info, +print_file (const char *filename, const struct grub_dirhook_info *info, void *data) { struct grub_ls_list_files_ctx *ctx = data; @@ -114,6 +102,12 @@ print_files_long (const char *filename, const struct grub_dirhook_info *info, if ((! ctx->all) && (filename[0] == '.')) return 0; + if (! ctx->longlist) + { + grub_printf ("%s%s ", filename, info->dir ? "/" : ""); + return 0; + } + if (! info->dir) { grub_file_t file; @@ -217,13 +211,11 @@ grub_ls_list_files (char *dirname, int longlist, int all, int human) struct grub_ls_list_files_ctx ctx = { .dirname = dirname, .all = all, - .human = human + .human = human, + .longlist = longlist }; - if (longlist) - (fs->fs_dir) (dev, path, print_files_long, &ctx); - else - (fs->fs_dir) (dev, path, print_files, &ctx); + (fs->fs_dir) (dev, path, print_file, &ctx); if (grub_errno == GRUB_ERR_BAD_FILE_TYPE && path[grub_strlen (path) - 1] != '/') @@ -251,10 +243,7 @@ grub_ls_list_files (char *dirname, int longlist, int all, int human) goto fail; grub_memset (&info, 0, sizeof (info)); - if (longlist) - print_files_long (p, &info, &ctx); - else - print_files (p, &info, &ctx); + print_file (p, &info, &ctx); grub_free (ctx.dirname); } From 6337d84afa247ada63dda65ca33a015ec9f800f6 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Sat, 1 Mar 2025 23:15:34 -0600 Subject: [PATCH 349/402] commands/ls: Show modification time for file paths The modification time for paths to files was not being printed because the grub_dirhook_info, which contains the mtime, was initialized to NULL. Instead of calling print_file() directly, use fs->fs_dir() to call print_file() with a properly filled in grub_dirhook_info. This has the added benefit of reducing code complexity. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- grub-core/commands/ls.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/grub-core/commands/ls.c b/grub-core/commands/ls.c index 2eaefaf90..1b447acf7 100644 --- a/grub-core/commands/ls.c +++ b/grub-core/commands/ls.c @@ -87,6 +87,7 @@ grub_ls_list_devices (int longlist) struct grub_ls_list_files_ctx { char *dirname; + char *filename; int all; int human; int longlist; @@ -102,6 +103,9 @@ print_file (const char *filename, const struct grub_dirhook_info *info, if ((! ctx->all) && (filename[0] == '.')) return 0; + if ((ctx->filename != NULL) && (grub_strcmp (filename, ctx->filename) != 0)) + return 0; + if (! ctx->longlist) { grub_printf ("%s%s ", filename, info->dir ? "/" : ""); @@ -210,6 +214,7 @@ grub_ls_list_files (char *dirname, int longlist, int all, int human) { struct grub_ls_list_files_ctx ctx = { .dirname = dirname, + .filename = NULL, .all = all, .human = human, .longlist = longlist @@ -220,30 +225,23 @@ grub_ls_list_files (char *dirname, int longlist, int all, int human) if (grub_errno == GRUB_ERR_BAD_FILE_TYPE && path[grub_strlen (path) - 1] != '/') { + /* + * Reset errno as it is currently set, but will cause subsequent code + * to think there is an error. + */ + grub_errno = GRUB_ERR_NONE; + /* PATH might be a regular file. */ - char *p; - grub_file_t file; - struct grub_dirhook_info info; - grub_errno = 0; - - file = grub_file_open (dirname, GRUB_FILE_TYPE_GET_SIZE - | GRUB_FILE_TYPE_NO_DECOMPRESS); - if (! file) + ctx.filename = grub_strrchr (dirname, '/'); + if (ctx.filename == NULL) goto fail; + ++(ctx.filename); - grub_file_close (file); - - p = grub_strrchr (dirname, '/'); - if (p == NULL) - goto fail; - ++p; - - ctx.dirname = grub_strndup (dirname, p - dirname); + ctx.dirname = grub_strndup (dirname, ctx.filename - dirname); if (ctx.dirname == NULL) goto fail; - grub_memset (&info, 0, sizeof (info)); - print_file (p, &info, &ctx); + (fs->fs_dir) (dev, ctx.dirname + (path - dirname), print_file, &ctx); grub_free (ctx.dirname); } From 90288fc48d15c9b39b89ef72d1ceb3e965d6d61d Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Sat, 1 Mar 2025 23:15:35 -0600 Subject: [PATCH 350/402] commands/ls: Output path for single file arguments given with path Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- grub-core/commands/ls.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grub-core/commands/ls.c b/grub-core/commands/ls.c index 1b447acf7..886441575 100644 --- a/grub-core/commands/ls.c +++ b/grub-core/commands/ls.c @@ -108,6 +108,8 @@ print_file (const char *filename, const struct grub_dirhook_info *info, if (! ctx->longlist) { + if (ctx->filename != NULL) + grub_xputs (ctx->dirname); grub_printf ("%s%s ", filename, info->dir ? "/" : ""); return 0; } From 096bf59e4c852d13cccec97511acfa2db7ee9980 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Sat, 1 Mar 2025 23:15:36 -0600 Subject: [PATCH 351/402] commands/ls: Print full paths for file args For arguments that are paths to files, print the full path of the file. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- grub-core/commands/ls.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/grub-core/commands/ls.c b/grub-core/commands/ls.c index 886441575..61c999991 100644 --- a/grub-core/commands/ls.c +++ b/grub-core/commands/ls.c @@ -98,6 +98,7 @@ static int print_file (const char *filename, const struct grub_dirhook_info *info, void *data) { + char *pathname = NULL; struct grub_ls_list_files_ctx *ctx = data; if ((! ctx->all) && (filename[0] == '.')) @@ -117,7 +118,6 @@ print_file (const char *filename, const struct grub_dirhook_info *info, if (! info->dir) { grub_file_t file; - char *pathname; if (ctx->dirname[grub_strlen (ctx->dirname) - 1] == '/') pathname = grub_xasprintf ("%s%s", ctx->dirname, filename); @@ -143,7 +143,6 @@ print_file (const char *filename, const struct grub_dirhook_info *info, else grub_xputs ("????????????"); - grub_free (pathname); grub_errno = GRUB_ERR_NONE; } else @@ -165,7 +164,16 @@ print_file (const char *filename, const struct grub_dirhook_info *info, datetime.day, datetime.hour, datetime.minute, datetime.second); } - grub_printf ("%s%s\n", filename, info->dir ? "/" : ""); + /* + * Only print the full path when listing a file path given as an argument + * to ls, i.e. when ctx->filename != NULL. File listings that are printed + * due to showing the contents of a directory do not need a full path because + * the full path to the directory will have already been printed. + */ + grub_printf ("%s%s\n", (ctx->filename != NULL) ? pathname : filename, + info->dir ? "/" : ""); + + grub_free (pathname); return 0; } From 488ac8bda9a8e33cad7096e6a09eb89da84aeb85 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Sat, 1 Mar 2025 23:15:37 -0600 Subject: [PATCH 352/402] commands/ls: Add directory header for dir args Like the GNU ls, first print a line with the directory path before printing files in the directory, which will not have a directory component, but only if there is more than one argument. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- grub-core/commands/ls.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/grub-core/commands/ls.c b/grub-core/commands/ls.c index 61c999991..c3bcb089a 100644 --- a/grub-core/commands/ls.c +++ b/grub-core/commands/ls.c @@ -91,6 +91,7 @@ struct grub_ls_list_files_ctx int all; int human; int longlist; + int print_dirhdr; }; /* Helper for grub_ls_list_files. */ @@ -107,6 +108,12 @@ print_file (const char *filename, const struct grub_dirhook_info *info, if ((ctx->filename != NULL) && (grub_strcmp (filename, ctx->filename) != 0)) return 0; + if (ctx->print_dirhdr) + { + grub_printf ("%s:\n", ctx->dirname); + ctx->print_dirhdr = 0; + } + if (! ctx->longlist) { if (ctx->filename != NULL) @@ -179,7 +186,7 @@ print_file (const char *filename, const struct grub_dirhook_info *info, } static grub_err_t -grub_ls_list_files (char *dirname, int longlist, int all, int human) +grub_ls_list_files (char *dirname, int longlist, int all, int human, int dirhdr) { char *device_name; grub_fs_t fs; @@ -227,7 +234,8 @@ grub_ls_list_files (char *dirname, int longlist, int all, int human) .filename = NULL, .all = all, .human = human, - .longlist = longlist + .longlist = longlist, + .print_dirhdr = dirhdr }; (fs->fs_dir) (dev, path, print_file, &ctx); @@ -242,6 +250,7 @@ grub_ls_list_files (char *dirname, int longlist, int all, int human) grub_errno = GRUB_ERR_NONE; /* PATH might be a regular file. */ + ctx.print_dirhdr = 0; ctx.filename = grub_strrchr (dirname, '/'); if (ctx.filename == NULL) goto fail; @@ -281,8 +290,8 @@ grub_cmd_ls (grub_extcmd_context_t ctxt, int argc, char **args) grub_ls_list_devices (state[0].set); else for (i = 0; i < argc; i++) - grub_ls_list_files (args[i], state[0].set, state[2].set, - state[1].set); + grub_ls_list_files (args[i], state[0].set, state[2].set, state[1].set, + argc > 1); return GRUB_ERR_NONE; } From 90848a1f7ce30d153d7636131d0c6a2d9fd7f242 Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Tue, 13 Aug 2024 20:40:02 -0400 Subject: [PATCH 353/402] disk/lvm: Make cache_lv more generic as ignored_feature_lv This patch isn't necessary by itself, but when combined with subsequent patches it enhances readability as ignored_features_lv is then used for multiple types of extra LV's, not just cache LV's. Signed-off-by: Patrick Plenefisch Reviewed-by: Daniel Kiper --- grub-core/disk/lvm.c | 176 +++++++++++++++++++++---------------------- 1 file changed, 88 insertions(+), 88 deletions(-) diff --git a/grub-core/disk/lvm.c b/grub-core/disk/lvm.c index d5af85482..97932fe9a 100644 --- a/grub-core/disk/lvm.c +++ b/grub-core/disk/lvm.c @@ -34,12 +34,12 @@ GRUB_MOD_LICENSE ("GPLv3+"); -struct cache_lv +struct ignored_feature_lv { struct grub_diskfilter_lv *lv; char *cache_pool; char *origin; - struct cache_lv *next; + struct ignored_feature_lv *next; }; @@ -105,30 +105,30 @@ grub_lvm_check_flag (const char *p, const char *str, const char *flag) } static void -grub_lvm_free_cache_lvs (struct cache_lv *cache_lvs) +grub_lvm_free_ignored_feature_lvs (struct ignored_feature_lv *ignored_feature_lvs) { - struct cache_lv *cache; + struct ignored_feature_lv *ignored_feature; - while ((cache = cache_lvs)) + while ((ignored_feature = ignored_feature_lvs)) { - cache_lvs = cache_lvs->next; + ignored_feature_lvs = ignored_feature_lvs->next; - if (cache->lv) + if (ignored_feature->lv) { unsigned int i; - for (i = 0; i < cache->lv->segment_count; ++i) - if (cache->lv->segments) - grub_free (cache->lv->segments[i].nodes); - grub_free (cache->lv->segments); - grub_free (cache->lv->fullname); - grub_free (cache->lv->idname); - grub_free (cache->lv->name); + for (i = 0; i < ignored_feature->lv->segment_count; ++i) + if (ignored_feature->lv->segments) + grub_free (ignored_feature->lv->segments[i].nodes); + grub_free (ignored_feature->lv->segments); + grub_free (ignored_feature->lv->fullname); + grub_free (ignored_feature->lv->idname); + grub_free (ignored_feature->lv->name); } - grub_free (cache->lv); - grub_free (cache->origin); - grub_free (cache->cache_pool); - grub_free (cache); + grub_free (ignored_feature->lv); + grub_free (ignored_feature->origin); + grub_free (ignored_feature->cache_pool); + grub_free (ignored_feature); } } @@ -325,7 +325,7 @@ grub_lvm_detect (grub_disk_t disk, if (! vg) { - struct cache_lv *cache_lvs = NULL; + struct ignored_feature_lv *ignored_feature_lvs = NULL; /* First time we see this volume group. We've to create the whole volume group structure. */ @@ -822,105 +822,105 @@ grub_lvm_detect (grub_disk_t disk, else if (grub_memcmp (p, "cache\"", sizeof ("cache\"") - 1) == 0) { - struct cache_lv *cache = NULL; + struct ignored_feature_lv *ignored_feature = NULL; char *p2, *p3; grub_size_t sz; - cache = grub_zalloc (sizeof (*cache)); - if (!cache) - goto cache_lv_fail; - cache->lv = grub_zalloc (sizeof (*cache->lv)); - if (!cache->lv) - goto cache_lv_fail; - grub_memcpy (cache->lv, lv, sizeof (*cache->lv)); + ignored_feature = grub_zalloc (sizeof (*ignored_feature)); + if (!ignored_feature) + goto ignored_feature_lv_fail; + ignored_feature->lv = grub_zalloc (sizeof (*ignored_feature->lv)); + if (!ignored_feature->lv) + goto ignored_feature_lv_fail; + grub_memcpy (ignored_feature->lv, lv, sizeof (*ignored_feature->lv)); if (lv->fullname) { - cache->lv->fullname = grub_strdup (lv->fullname); - if (!cache->lv->fullname) - goto cache_lv_fail; + ignored_feature->lv->fullname = grub_strdup (lv->fullname); + if (!ignored_feature->lv->fullname) + goto ignored_feature_lv_fail; } if (lv->idname) { - cache->lv->idname = grub_strdup (lv->idname); - if (!cache->lv->idname) - goto cache_lv_fail; + ignored_feature->lv->idname = grub_strdup (lv->idname); + if (!ignored_feature->lv->idname) + goto ignored_feature_lv_fail; } if (lv->name) { - cache->lv->name = grub_strdup (lv->name); - if (!cache->lv->name) - goto cache_lv_fail; + ignored_feature->lv->name = grub_strdup (lv->name); + if (!ignored_feature->lv->name) + goto ignored_feature_lv_fail; } skip_lv = 1; p2 = grub_strstr (p, "cache_pool = \""); if (!p2) - goto cache_lv_fail; + goto ignored_feature_lv_fail; p2 = grub_strchr (p2, '"'); if (!p2) - goto cache_lv_fail; + goto ignored_feature_lv_fail; p3 = ++p2; if (p3 == mda_end) - goto cache_lv_fail; + goto ignored_feature_lv_fail; p3 = grub_strchr (p3, '"'); if (!p3) - goto cache_lv_fail; + goto ignored_feature_lv_fail; sz = p3 - p2; - cache->cache_pool = grub_malloc (sz + 1); - if (!cache->cache_pool) - goto cache_lv_fail; - grub_memcpy (cache->cache_pool, p2, sz); - cache->cache_pool[sz] = '\0'; + ignored_feature->cache_pool = grub_malloc (sz + 1); + if (!ignored_feature->cache_pool) + goto ignored_feature_lv_fail; + grub_memcpy (ignored_feature->cache_pool, p2, sz); + ignored_feature->cache_pool[sz] = '\0'; p2 = grub_strstr (p, "origin = \""); if (!p2) - goto cache_lv_fail; + goto ignored_feature_lv_fail; p2 = grub_strchr (p2, '"'); if (!p2) - goto cache_lv_fail; + goto ignored_feature_lv_fail; p3 = ++p2; if (p3 == mda_end) - goto cache_lv_fail; + goto ignored_feature_lv_fail; p3 = grub_strchr (p3, '"'); if (!p3) - goto cache_lv_fail; + goto ignored_feature_lv_fail; sz = p3 - p2; - cache->origin = grub_malloc (sz + 1); - if (!cache->origin) - goto cache_lv_fail; - grub_memcpy (cache->origin, p2, sz); - cache->origin[sz] = '\0'; + ignored_feature->origin = grub_malloc (sz + 1); + if (!ignored_feature->origin) + goto ignored_feature_lv_fail; + grub_memcpy (ignored_feature->origin, p2, sz); + ignored_feature->origin[sz] = '\0'; - cache->next = cache_lvs; - cache_lvs = cache; + ignored_feature->next = ignored_feature_lvs; + ignored_feature_lvs = ignored_feature; break; - cache_lv_fail: - if (cache) + ignored_feature_lv_fail: + if (ignored_feature) { - grub_free (cache->origin); - grub_free (cache->cache_pool); - if (cache->lv) + grub_free (ignored_feature->origin); + grub_free (ignored_feature->cache_pool); + if (ignored_feature->lv) { - grub_free (cache->lv->fullname); - grub_free (cache->lv->idname); - grub_free (cache->lv->name); + grub_free (ignored_feature->lv->fullname); + grub_free (ignored_feature->lv->idname); + grub_free (ignored_feature->lv->name); } - grub_free (cache->lv); - grub_free (cache); + grub_free (ignored_feature->lv); + grub_free (ignored_feature); } - grub_lvm_free_cache_lvs (cache_lvs); + grub_lvm_free_ignored_feature_lvs (ignored_feature_lvs); goto fail4; } else @@ -1005,56 +1005,56 @@ grub_lvm_detect (grub_disk_t disk, } { - struct cache_lv *cache; + struct ignored_feature_lv *ignored_feature; - for (cache = cache_lvs; cache; cache = cache->next) + for (ignored_feature = ignored_feature_lvs; ignored_feature; ignored_feature = ignored_feature->next) { struct grub_diskfilter_lv *lv; for (lv = vg->lvs; lv; lv = lv->next) - if (grub_strcmp (lv->name, cache->origin) == 0) + if (grub_strcmp (lv->name, ignored_feature->origin) == 0) break; if (lv) { - cache->lv->segments = grub_calloc (lv->segment_count, sizeof (*lv->segments)); - if (!cache->lv->segments) + ignored_feature->lv->segments = grub_calloc (lv->segment_count, sizeof (*lv->segments)); + if (!ignored_feature->lv->segments) { - grub_lvm_free_cache_lvs (cache_lvs); + grub_lvm_free_ignored_feature_lvs (ignored_feature_lvs); goto fail4; } - grub_memcpy (cache->lv->segments, lv->segments, lv->segment_count * sizeof (*lv->segments)); + grub_memcpy (ignored_feature->lv->segments, lv->segments, lv->segment_count * sizeof (*lv->segments)); for (i = 0; i < lv->segment_count; ++i) { struct grub_diskfilter_node *nodes = lv->segments[i].nodes; grub_size_t node_count = lv->segments[i].node_count; - cache->lv->segments[i].nodes = grub_calloc (node_count, sizeof (*nodes)); - if (!cache->lv->segments[i].nodes) + ignored_feature->lv->segments[i].nodes = grub_calloc (node_count, sizeof (*nodes)); + if (!ignored_feature->lv->segments[i].nodes) { for (j = 0; j < i; ++j) - grub_free (cache->lv->segments[j].nodes); - grub_free (cache->lv->segments); - cache->lv->segments = NULL; - grub_lvm_free_cache_lvs (cache_lvs); + grub_free (ignored_feature->lv->segments[j].nodes); + grub_free (ignored_feature->lv->segments); + ignored_feature->lv->segments = NULL; + grub_lvm_free_ignored_feature_lvs (ignored_feature_lvs); goto fail4; } - grub_memcpy (cache->lv->segments[i].nodes, nodes, node_count * sizeof (*nodes)); + grub_memcpy (ignored_feature->lv->segments[i].nodes, nodes, node_count * sizeof (*nodes)); } - if (cache->lv->segments) + if (ignored_feature->lv->segments) { - cache->lv->segment_count = lv->segment_count; - cache->lv->vg = vg; - cache->lv->next = vg->lvs; - vg->lvs = cache->lv; - cache->lv = NULL; + ignored_feature->lv->segment_count = lv->segment_count; + ignored_feature->lv->vg = vg; + ignored_feature->lv->next = vg->lvs; + vg->lvs = ignored_feature->lv; + ignored_feature->lv = NULL; } } } } - grub_lvm_free_cache_lvs (cache_lvs); + grub_lvm_free_ignored_feature_lvs (ignored_feature_lvs); if (grub_diskfilter_vg_register (vg)) goto fail4; } From d34b9120e7dd4c860e103545f57ad8083aea4fea Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Tue, 13 Aug 2024 20:15:37 -0400 Subject: [PATCH 354/402] disk/lvm: Remove unused cache_pool The cache_pool is never read or used, remove it. Signed-off-by: Patrick Plenefisch Reviewed-by: Daniel Kiper --- grub-core/disk/lvm.c | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/grub-core/disk/lvm.c b/grub-core/disk/lvm.c index 97932fe9a..f17b43655 100644 --- a/grub-core/disk/lvm.c +++ b/grub-core/disk/lvm.c @@ -37,7 +37,6 @@ GRUB_MOD_LICENSE ("GPLv3+"); struct ignored_feature_lv { struct grub_diskfilter_lv *lv; - char *cache_pool; char *origin; struct ignored_feature_lv *next; }; @@ -127,7 +126,6 @@ grub_lvm_free_ignored_feature_lvs (struct ignored_feature_lv *ignored_feature_lv } grub_free (ignored_feature->lv); grub_free (ignored_feature->origin); - grub_free (ignored_feature->cache_pool); grub_free (ignored_feature); } } @@ -856,28 +854,6 @@ grub_lvm_detect (grub_disk_t disk, skip_lv = 1; - p2 = grub_strstr (p, "cache_pool = \""); - if (!p2) - goto ignored_feature_lv_fail; - - p2 = grub_strchr (p2, '"'); - if (!p2) - goto ignored_feature_lv_fail; - - p3 = ++p2; - if (p3 == mda_end) - goto ignored_feature_lv_fail; - p3 = grub_strchr (p3, '"'); - if (!p3) - goto ignored_feature_lv_fail; - - sz = p3 - p2; - - ignored_feature->cache_pool = grub_malloc (sz + 1); - if (!ignored_feature->cache_pool) - goto ignored_feature_lv_fail; - grub_memcpy (ignored_feature->cache_pool, p2, sz); - ignored_feature->cache_pool[sz] = '\0'; p2 = grub_strstr (p, "origin = \""); if (!p2) @@ -910,7 +886,6 @@ grub_lvm_detect (grub_disk_t disk, if (ignored_feature) { grub_free (ignored_feature->origin); - grub_free (ignored_feature->cache_pool); if (ignored_feature->lv) { grub_free (ignored_feature->lv->fullname); From 6c14b87d6fe03972f7a24689e51008698aedba15 Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Sat, 4 Jan 2025 15:00:36 -0500 Subject: [PATCH 355/402] lvm: Match all LVM segments before validation The PV matching must be completely finished before validating a volume, otherwise referenced RAID stripes may not have PV data applied yet. This change is required for integrity & cachevol support. Signed-off-by: Patrick Plenefisch Reviewed-by: Daniel Kiper --- grub-core/disk/diskfilter.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/grub-core/disk/diskfilter.c b/grub-core/disk/diskfilter.c index 78d6a15db..cc5062691 100644 --- a/grub-core/disk/diskfilter.c +++ b/grub-core/disk/diskfilter.c @@ -980,8 +980,6 @@ grub_diskfilter_vg_register (struct grub_diskfilter_vg *vg) for (lv = vg->lvs; lv; lv = lv->next) { - grub_err_t err; - /* RAID 1 and single-disk RAID 0 don't use a chunksize but code assumes one so set one. */ for (i = 0; i < lv->segment_count; i++) @@ -993,6 +991,10 @@ grub_diskfilter_vg_register (struct grub_diskfilter_vg *vg) && lv->segments[i].stripe_size == 0) lv->segments[i].stripe_size = 64; } + } + for (lv = vg->lvs; lv; lv = lv->next) + { + grub_err_t err; err = validate_lv(lv); if (err) From 9a37d6114593417bbd29b2d2c3794c05305e9ebe Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Sat, 4 Jan 2025 15:02:54 -0500 Subject: [PATCH 356/402] disk/lvm: Add support for integrity LV The LV matching must be done after processing the ignored feature indirections, as integrity volumes & caches may have several levels of indirection that the segments must be shifted through. Signed-off-by: Patrick Plenefisch Reviewed-by: Daniel Kiper --- grub-core/disk/lvm.c | 69 ++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/grub-core/disk/lvm.c b/grub-core/disk/lvm.c index f17b43655..14839a65b 100644 --- a/grub-core/disk/lvm.c +++ b/grub-core/disk/lvm.c @@ -817,8 +817,12 @@ grub_lvm_detect (grub_disk_t disk, seg->nodes[seg->node_count - 1].name = tmp; } } - else if (grub_memcmp (p, "cache\"", - sizeof ("cache\"") - 1) == 0) + /* + * Cache and integrity LVs have extra parts that + * we can ignore for our read-only access. + */ + else if (grub_strncmp (p, "cache\"", sizeof ("cache\"") - 1) == 0 || + grub_strncmp (p, "integrity\"", sizeof ("integrity\"") - 1) == 0) { struct ignored_feature_lv *ignored_feature = NULL; @@ -948,36 +952,6 @@ grub_lvm_detect (grub_disk_t disk, } } - /* Match lvs. */ - { - struct grub_diskfilter_lv *lv1; - struct grub_diskfilter_lv *lv2; - for (lv1 = vg->lvs; lv1; lv1 = lv1->next) - for (i = 0; i < lv1->segment_count; i++) - for (j = 0; j < lv1->segments[i].node_count; j++) - { - if (vg->pvs) - for (pv = vg->pvs; pv; pv = pv->next) - { - if (! grub_strcmp (pv->name, - lv1->segments[i].nodes[j].name)) - { - lv1->segments[i].nodes[j].pv = pv; - break; - } - } - if (lv1->segments[i].nodes[j].pv == NULL) - for (lv2 = vg->lvs; lv2; lv2 = lv2->next) - { - if (lv1 == lv2) - continue; - if (grub_strcmp (lv2->name, - lv1->segments[i].nodes[j].name) == 0) - lv1->segments[i].nodes[j].lv = lv2; - } - } - - } { struct ignored_feature_lv *ignored_feature; @@ -1029,6 +1003,37 @@ grub_lvm_detect (grub_disk_t disk, } } + /* Match LVs. Must be done after cache and integrity are found. */ + { + struct grub_diskfilter_lv *lv1; + struct grub_diskfilter_lv *lv2; + + for (lv1 = vg->lvs; lv1; lv1 = lv1->next) + for (i = 0; i < lv1->segment_count; i++) + for (j = 0; j < lv1->segments[i].node_count; j++) + { + if (vg->pvs) + for (pv = vg->pvs; pv; pv = pv->next) + { + if (! grub_strcmp (pv->name, + lv1->segments[i].nodes[j].name)) + { + lv1->segments[i].nodes[j].pv = pv; + break; + } + } + if (lv1->segments[i].nodes[j].pv == NULL) + for (lv2 = vg->lvs; lv2; lv2 = lv2->next) + { + if (lv1 == lv2) + continue; + if (grub_strcmp (lv2->name, + lv1->segments[i].nodes[j].name) == 0) + lv1->segments[i].nodes[j].lv = lv2; + } + } + } + grub_lvm_free_ignored_feature_lvs (ignored_feature_lvs); if (grub_diskfilter_vg_register (vg)) goto fail4; From a16b4304a2b4f2b5ff57e247725389475d2c52dd Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Mon, 11 Nov 2024 13:18:39 -0500 Subject: [PATCH 357/402] disk/lvm: Add support for cachevol LV Mark cachevol LV's as ignored features, which is true only if they are configured as "writethrough". This patch does not let GRUB boot from "writeback" cache-enabled LV's. Signed-off-by: Patrick Plenefisch Reviewed-by: Daniel Kiper --- grub-core/disk/lvm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/grub-core/disk/lvm.c b/grub-core/disk/lvm.c index 14839a65b..1f8e5d76d 100644 --- a/grub-core/disk/lvm.c +++ b/grub-core/disk/lvm.c @@ -822,6 +822,7 @@ grub_lvm_detect (grub_disk_t disk, * we can ignore for our read-only access. */ else if (grub_strncmp (p, "cache\"", sizeof ("cache\"") - 1) == 0 || + grub_strncmp (p, "cache+CACHE_USES_CACHEVOL\"", sizeof ("cache+CACHE_USES_CACHEVOL\"") - 1) == 0 || grub_strncmp (p, "integrity\"", sizeof ("integrity\"") - 1) == 0) { struct ignored_feature_lv *ignored_feature = NULL; From ff7f5530713c203c90a7dea2b7c56d403673548b Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Sat, 7 Dec 2024 21:19:23 -0500 Subject: [PATCH 358/402] disk/lvm: Add informational messages in error cases of ignored features Signed-off-by: Patrick Plenefisch Reviewed-by: Daniel Kiper --- grub-core/disk/lvm.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/grub-core/disk/lvm.c b/grub-core/disk/lvm.c index 1f8e5d76d..af6a8e93c 100644 --- a/grub-core/disk/lvm.c +++ b/grub-core/disk/lvm.c @@ -829,6 +829,14 @@ grub_lvm_detect (grub_disk_t disk, char *p2, *p3; grub_size_t sz; +#ifdef GRUB_UTIL + p2 = grub_strchr (p, '"'); + if (p2) + *p2 = '\0'; + grub_util_info ("Ignoring extra metadata type '%s' for %s", p, lv->name); + if (p2) + *p2 ='"'; +#endif ignored_feature = grub_zalloc (sizeof (*ignored_feature)); if (!ignored_feature) @@ -909,7 +917,7 @@ grub_lvm_detect (grub_disk_t disk, char *p2; p2 = grub_strchr (p, '"'); if (p2) - *p2 = 0; + *p2 = '\0'; grub_util_info ("unknown LVM type %s", p); if (p2) *p2 ='"'; @@ -1001,6 +1009,12 @@ grub_lvm_detect (grub_disk_t disk, ignored_feature->lv = NULL; } } + else + { +#ifdef GRUB_UTIL + grub_util_info ("Couldn't find LVM part of ignored feature on %s", ignored_feature->origin); +#endif + } } } From 3fd163e453b577b6728babe80f8f77c981a67b08 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Thu, 6 Mar 2025 00:48:13 -0600 Subject: [PATCH 359/402] tests/util/grub-shell: Default qemuopts to envvar $GRUB_QEMU_OPTS Fix a regression where qemuopts was mistakenly defaulted to the empty string. This prevents the sending of arbitrary QEMU options to tests, which may be desirable for overriding the machine type. There was a concern that allowing the tester to accept arbitrary options would add headaches for another developer trying to diagnose why a test failed on the testers machine because he could not be sure if any additional options were passed to make the test fail. However, all the options are recorded in the run.sh generated script, so this concern is unwarranted. Fixes: 6d729ced70 (tests/util/grub-shell: Add $GRUB_QEMU_OPTS to run.sh to easily see unofficial QEMU arguments) Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- tests/util/grub-shell.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/util/grub-shell.in b/tests/util/grub-shell.in index 15c5f45a5..dae4ff35a 100644 --- a/tests/util/grub-shell.in +++ b/tests/util/grub-shell.in @@ -74,7 +74,7 @@ exec_show_error () { work_directory=${WORKDIR:-`mktemp -d "${TMPDIR:-/tmp}/grub-shell.XXXXXXXXXX"`} || exit 1 . "${builddir}/grub-core/modinfo.sh" -qemuopts= +qemuopts=${GRUB_QEMU_OPTS} emuopts= serial_port=com0 serial_null= From bb6d3199b384d400fb87bf96dfddb95f5e6b08b5 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Thu, 6 Mar 2025 00:18:43 -0600 Subject: [PATCH 360/402] tests/util/grub-shell-luks-tester: Find cryptodisk by UUID GRUB has the capability to search all the disks for a cryptodisk of a given UUID. Use this instead of hardcoding which disk is the cryptodisk, which can change when devices are added or removed, or potentially when QEMU is upgraded. This can not be done for the detached header tests because the header contains the UUID. Also, capitalize comment lines for consistency. Signed-off-by: Glenn Washburn Reviewed-by: Daniel Kiper --- tests/util/grub-shell-luks-tester.in | 35 +++++++++++++--------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/tests/util/grub-shell-luks-tester.in b/tests/util/grub-shell-luks-tester.in index b2a8a91b4..7cfb60b51 100644 --- a/tests/util/grub-shell-luks-tester.in +++ b/tests/util/grub-shell-luks-tester.in @@ -30,6 +30,7 @@ PATH="${builddir}:$PATH" export PATH grub_shell_opts= +disk= disksize=20M detached_header= keyfile= @@ -68,18 +69,6 @@ EOF . "${builddir}/grub-core/modinfo.sh" -# TODO: We should be selecting the drive based on disk id, change this once -# grub support searching by disk id. -disk="hd0" -case "${grub_modinfo_target_cpu}-${grub_modinfo_platform}" in - i386-qemu) - disk="ata0" - ;; - loongarch64-efi) - disk="hd1" - ;; -esac - # Check the arguments. for option in "$@"; do case "$option" in @@ -158,7 +147,7 @@ get_random_bytes() { | tr -d '\0' | dd bs=1 count=$(($NUM_BYTES)) 2>/dev/null } -# create a random directory to be hold generated files +# Create a random directory to be hold generated files lukstestdir="`mktemp -d "${TMPDIR:-/tmp}/$(basename "$0").XXXXXXXXXX"`" || exit 20 luksfile=$lukstestdir/luks.disk lukshdrfile=$lukstestdir/luks.header @@ -185,14 +174,21 @@ if [ -n "$detached_header" ]; then csopts="$csopts --header $lukshdrfile" fi -# create the key file +# Create the key file echo -n "$password" > $lukskeyfile # Create a very small LUKS container for the test truncate -s $disksize $luksfile || exit 21 # Format the luks disk file -cryptsetup luksFormat -q $csopts $luksfile $lukskeyfile || exit 22 +cryptsetup luksFormat -q $csopts ${luksfile} ${lukskeyfile} || exit 22 +if [ -z "$detached_header" ]; then + cryptomount_opts="$cryptomount_opts -u $(cryptsetup luksUUID ${luksdiskfile})" +elif [ -z "$disk" ]; then + # In detached header mode, so must pass the disk to cryptomount. + # Is this always correct? + disk=hd0 +fi # Run any cryptsetup scripts export luksdiskfile @@ -264,7 +260,7 @@ fi # Can not use --disk with a raw LUKS container because it appears qemu # tries to convert the image to and is failing with: # "Parameter 'key-secret' is required for cipher" -qemuopts="$qemuopts -drive file=$luksfile,index=0,media=disk,format=raw" +qemuopts="$qemuopts -drive file=$luksfile,media=disk,format=raw" # Add crypto modules modules="$modules cryptodisk luks luks2 fat" @@ -282,7 +278,8 @@ grub_keyfile_size="$keyfile_size" vfilename="`basename $vfile`" vtext="$vtext" trim_line="$trim_line" -disk="$disk" +disk="${disk:+($disk)}" +cryptomount_opts="$cryptomount_opts" EOF # If testing keyfiles, do not use password variable @@ -325,7 +322,7 @@ fi cdisk=crypto0 if test -n "$grub_debug" -a "$grub_debug" -gt 0; then - echo cmd: cryptomount $cryptomount_opts ($disk) + echo cmd: cryptomount $cryptomount_opts $disk echo -n "devices: " ls fi @@ -333,7 +330,7 @@ fi if test -n "$grub_debug" -a "$grub_debug" -gt 1; then set debug=all fi -cryptomount $cryptomount_opts ($disk) +cryptomount $cryptomount_opts $disk ret="$?" if test -n "$grub_debug" -a "$grub_debug" -eq 2; then set debug= From 50320c093abe29dfa5adc349529128425bade56a Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 3 Mar 2025 02:12:01 -0600 Subject: [PATCH 361/402] tests/util/grub-shell-luks-tester: Add missing line to create RET variable in cleanup Set the RET variable to the exit status of the script, as was assumed in the cleanup() function. Reported-by: Thomas Schmitt Signed-off-by: Glenn Washburn Tested-by: Thomas Schmitt Reviewed-by: Daniel Kiper --- tests/util/grub-shell-luks-tester.in | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/util/grub-shell-luks-tester.in b/tests/util/grub-shell-luks-tester.in index 7cfb60b51..e695b9435 100644 --- a/tests/util/grub-shell-luks-tester.in +++ b/tests/util/grub-shell-luks-tester.in @@ -132,6 +132,7 @@ fi # Make sure that the dm-crypto device is shutdown cleanup() { + RET=$? if [ -e "$luksdev" ]; then cryptsetup close "$luksdev" fi From c188ca5d5ee692d15a433b4e2a44d2deb6b680a2 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 3 Mar 2025 02:12:02 -0600 Subject: [PATCH 362/402] tests: Cleanup generated files on expected failure in grub_cmd_cryptomount grub-shell-luks-tester only cleans up generated files when the test it runs returns success. Sometimes tests are run that should fail. Add a --xfail argument to grub-shell-luks-tester and pass it from grub_cmd_cryptomount when invoking a test that is expected to fail. Reported-by: Thomas Schmitt Signed-off-by: Glenn Washburn Tested-by: Thomas Schmitt Reviewed-by: Daniel Kiper --- tests/grub_cmd_cryptomount.in | 1 + tests/util/grub-shell-luks-tester.in | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/grub_cmd_cryptomount.in b/tests/grub_cmd_cryptomount.in index f4d8f3547..63ed8e51b 100644 --- a/tests/grub_cmd_cryptomount.in +++ b/tests/grub_cmd_cryptomount.in @@ -48,6 +48,7 @@ _testcase() { _TMPDIR=$TMPDIR TMPDIR=$TMPDIR/`echo -n "$(date +%s).$LOGPREFIX" | sed -e 's,[ /],_,g' -e 's,:$,,g'` mkdir -p "$TMPDIR" + set -- "$@" $([ "${EXPECTEDRES}" -eq 1 ] && echo "--xfail") output=`"$@" 2>&1` || res=$? TMPDIR=$_TMPDIR diff --git a/tests/util/grub-shell-luks-tester.in b/tests/util/grub-shell-luks-tester.in index e695b9435..aa454d136 100644 --- a/tests/util/grub-shell-luks-tester.in +++ b/tests/util/grub-shell-luks-tester.in @@ -37,6 +37,7 @@ keyfile= keyfile_offset= keyfile_size= KEYFILE_SIZE_MAX=4096 +expected_res=0 debug="${GRUB_SHELL_LUKS_DEFAULT_DEBUG:-$GRUB_TEST_DEFAULT_DEBUG}" GRUB_SHELL_LUKS_TIMEOUT=${GRUB_SHELL_LUKS_TIMEOUT:-${GRUB_SHELL_DEFAULT_TIMEOUT:-600s}} @@ -59,6 +60,7 @@ running in a QEMU instance. --detached-header Use a detached header --keyfile[=FILE] Use a randomly generated key file of size $KEYFILE_SIZE_MAX if not given a FILE to use as the key file. + --xfail Expected failure, so cleanup as if on success $0 creates a LUKS disk with cryptsetup, then verify that it is accessible by grub running in a QEMU instance. @@ -107,6 +109,8 @@ for option in "$@"; do --disksize=*) qs=`echo "$option" | sed -e 's/--disksize=//'` disksize="$qs" ;; + --xfail) + expected_res=1 ;; -*) echo "Unrecognized option \`$option'" 1>&2 usage @@ -136,7 +140,7 @@ cleanup() { if [ -e "$luksdev" ]; then cryptsetup close "$luksdev" fi - if [ -z "$debug" ] && [ "${RET:-1}" -eq 0 ]; then + if [ -z "$debug" ] && [ "$RET" -eq "$expected_res" ]; then rm -rf "$lukstestdir" || : fi } From 32b02bb92eb12be8391c7269346692119cb4c441 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 3 Mar 2025 02:12:03 -0600 Subject: [PATCH 363/402] tests/grub_cmd_cryptomount: Cleanup the cryptsetup script unless debug is enabled This fixes an issue where the grub_cmd_cryptomount test leaves a file with an ambiguous name in the / directory when TMPDIR is not set. Reported-by: Thomas Schmitt Signed-off-by: Glenn Washburn Tested-by: Thomas Schmitt Reviewed-by: Daniel Kiper --- tests/grub_cmd_cryptomount.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/grub_cmd_cryptomount.in b/tests/grub_cmd_cryptomount.in index 63ed8e51b..2fba8a8e0 100644 --- a/tests/grub_cmd_cryptomount.in +++ b/tests/grub_cmd_cryptomount.in @@ -37,6 +37,8 @@ fi COMMON_OPTS='${V:+--debug=$V} --cs-opts="--pbkdf-force-iterations 1000"' +debug=${GRUB_SHELL_DEFAULT_DEBUG:-$GRUB_TEST_DEFAULT_DEBUG} + _testcase() { local EXPECTEDRES=$1 local LOGPREFIX=$2 @@ -183,4 +185,5 @@ eval testcase "'LUKS2 test with second key slot and first slot using different p @builddir@/grub-shell-luks-tester $LUKS2_COMMON_OPTS $COMMON_OPTS \ "--cs-script='$csscript'" +test -n "$debug" || rm "$csscript" exit 0 From e6e2b73db8004e122a06a253107cef2343b6e092 Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 3 Mar 2025 02:12:04 -0600 Subject: [PATCH 364/402] tests/grub_cmd_cryptomount: Default TMPDIR to /tmp This fixes behavior where grub_cmd_cryptomount temporary files, which are some times not cleaned up, are left in the / directory. Set TMPDIR if your system does not have /tmp or it can not be used for some reason. Reported-by: Thomas Schmitt Signed-off-by: Glenn Washburn Tested-by: Thomas Schmitt Reviewed-by: Daniel Kiper --- tests/grub_cmd_cryptomount.in | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/grub_cmd_cryptomount.in b/tests/grub_cmd_cryptomount.in index 2fba8a8e0..8dc99127d 100644 --- a/tests/grub_cmd_cryptomount.in +++ b/tests/grub_cmd_cryptomount.in @@ -47,8 +47,9 @@ _testcase() { shift 2 # Create a subdir in TMPDIR for each testcase - _TMPDIR=$TMPDIR - TMPDIR=$TMPDIR/`echo -n "$(date +%s).$LOGPREFIX" | sed -e 's,[ /],_,g' -e 's,:$,,g'` + _TMPDIR=${TMPDIR:-/tmp} + TMPDIR=${_TMPDIR}/`echo -n "$(date +%s).${LOGPREFIX}" | sed -e 's,[ /],_,g' -e 's,:$,,g'` + export TMPDIR mkdir -p "$TMPDIR" set -- "$@" $([ "${EXPECTEDRES}" -eq 1 ] && echo "--xfail") From e0116f3bd515e5857844ac6c543c4bb90e6f5d70 Mon Sep 17 00:00:00 2001 From: Thomas Schmitt Date: Mon, 3 Mar 2025 02:12:05 -0600 Subject: [PATCH 365/402] tests/grub_cmd_cryptomount: Remove temporary directories if successful and debug is not on grub_cmd_cryptomount creates a directory per subtest. If a subtest is successful and debugging is not on, the directory should be empty. So, it can be deleted. Signed-off-by: Thomas Schmitt Signed-off-by: Glenn Washburn Tested-by: Thomas Schmitt Reviewed-by: Daniel Kiper --- tests/grub_cmd_cryptomount.in | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/grub_cmd_cryptomount.in b/tests/grub_cmd_cryptomount.in index 8dc99127d..eaa187efa 100644 --- a/tests/grub_cmd_cryptomount.in +++ b/tests/grub_cmd_cryptomount.in @@ -54,6 +54,15 @@ _testcase() { set -- "$@" $([ "${EXPECTEDRES}" -eq 1 ] && echo "--xfail") output=`"$@" 2>&1` || res=$? + if [ -z "$debug" ]; then + if ! rmdir "$TMPDIR" >/dev/null 2>&1; then + echo + echo "Note: Temporary directory cannot be removed:" + echo " $TMPDIR" + echo " Please inspect and remove manually." + echo + fi + fi TMPDIR=$_TMPDIR if [ "$res" -eq "$EXPECTEDRES" ]; then From 1d59f39b5f1b407560ebf4d8f9bd45054a2c297c Mon Sep 17 00:00:00 2001 From: Glenn Washburn Date: Mon, 3 Mar 2025 02:12:06 -0600 Subject: [PATCH 366/402] tests/util/grub-shell: Remove the work directory on successful run and debug is not on This removes a lot of empty grub-shell working directories in the TMPDIR directory. Signed-off-by: Thomas Schmitt Signed-off-by: Glenn Washburn Tested-by: Thomas Schmitt Reviewed-by: Daniel Kiper --- tests/util/grub-shell.in | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/util/grub-shell.in b/tests/util/grub-shell.in index dae4ff35a..8baa3667a 100644 --- a/tests/util/grub-shell.in +++ b/tests/util/grub-shell.in @@ -715,6 +715,7 @@ test -n "$debug" || rm -f "${isofile}" test -n "$debug" || rm -rf "${rom_directory}" test -n "$debug" || rm -f "${tmpfile}" "${cfgfile}" "${goutfile}" test -n "$debug" || rm -f "$work_directory/run.sh" +test -n "$debug" || rmdir "$work_directory" exit $ret From b66c6f9182fce873d906bc9871c649af38e35b53 Mon Sep 17 00:00:00 2001 From: Stuart Hayes Date: Mon, 10 Mar 2025 11:23:59 -0500 Subject: [PATCH 367/402] fs/zfs: Fix a number of memory leaks in ZFS code Without this fix the GRUB failed to boot linux with "out of memory" after trying to run a "search --fs-uuid..." on a system that has 7 ZFS pools across about 80 drives. Signed-off-by: Stuart Hayes Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/fs/zfs/zfs.c | 43 +++++++++++++++++++++++++++++++++----- grub-core/fs/zfs/zfsinfo.c | 5 ++++- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index 376042631..bff9d0208 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -1057,8 +1057,10 @@ check_pool_label (struct grub_zfs_data *data, ZIO_SET_CHECKSUM(&emptycksum, diskdesc->vdev_phys_sector << 9, 0, 0, 0); err = zio_checksum_verify (emptycksum, ZIO_CHECKSUM_LABEL, endian, nvlist, VDEV_PHYS_SIZE); - if (err) + if (err) { + grub_free (nvlist); return err; + } grub_dprintf ("zfs", "check 2 passed\n"); @@ -1144,8 +1146,10 @@ check_pool_label (struct grub_zfs_data *data, if (original) data->guid = poolguid; - if (data->guid != poolguid) + if (data->guid != poolguid) { + grub_free (nvlist); return grub_error (GRUB_ERR_BAD_FS, "another zpool"); + } { char *nv; @@ -1186,9 +1190,12 @@ check_pool_label (struct grub_zfs_data *data, { grub_dprintf("zfs","feature missing in check_pool_label:%s\n",name); err= grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET," check_pool_label missing feature '%s' for read",name); + grub_free(features); + grub_free(nvlist); return err; } } + grub_free(features); } grub_dprintf ("zfs", "check 12 passed (feature flags)\n"); grub_free (nvlist); @@ -2601,6 +2608,7 @@ zap_lookup (dnode_end_t * zap_dnode, const char *name, grub_uint64_t *val, return err; } + grub_free (zapbuf); return grub_error (GRUB_ERR_BAD_FS, "unknown ZAP type"); } @@ -2671,6 +2679,7 @@ zap_iterate_u64 (dnode_end_t * zap_dnode, grub_free (zapbuf); return ret; } + grub_free (zapbuf); grub_error (GRUB_ERR_BAD_FS, "unknown ZAP type"); return 0; } @@ -2701,6 +2710,7 @@ zap_iterate (dnode_end_t * zap_dnode, if (block_type == ZBT_MICRO) { grub_error (GRUB_ERR_BAD_FS, "micro ZAP where FAT ZAP expected"); + grub_free (zapbuf); return 0; } if (block_type == ZBT_HEADER) @@ -2712,6 +2722,7 @@ zap_iterate (dnode_end_t * zap_dnode, grub_free (zapbuf); return ret; } + grub_free (zapbuf); grub_error (GRUB_ERR_BAD_FS, "unknown ZAP type"); return 0; } @@ -2785,6 +2796,9 @@ dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type, } grub_memmove (&(buf->dn), (dnode_phys_t *) dnbuf + idx, DNODE_SIZE); + if (data->dnode_buf == 0) + /* dnbuf not used anymore if data->dnode_mdn malloc failed */ + grub_free (dnbuf); buf->endian = endian; if (type && buf->dn.dn_type != type) return grub_error(GRUB_ERR_BAD_FS, "incorrect dnode type"); @@ -3436,6 +3450,8 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol, if (err) { grub_dprintf ("zfs", "failed here\n"); + grub_free (fsname); + grub_free (snapname); return err; } @@ -3472,8 +3488,11 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol, if (!err) err = dnode_get (&(data->mos), headobj, 0, &subvol->mdn, data); - if (!err && subvol->mdn.dn.dn_type != DMU_OT_DSL_DATASET && subvol->mdn.dn.dn_bonustype != DMU_OT_DSL_DATASET) + if (!err && subvol->mdn.dn.dn_type != DMU_OT_DSL_DATASET && subvol->mdn.dn.dn_bonustype != DMU_OT_DSL_DATASET) { + grub_free (fsname); + grub_free (snapname); return grub_error(GRUB_ERR_BAD_FS, "incorrect dataset dnode type"); + } if (err) { @@ -3952,6 +3971,7 @@ grub_zfs_open (struct grub_file *file, const char *fsfilename) { void *sahdrp; int hdrsize; + bool free_sahdrp = false; if (data->dnode.dn.dn_bonuslen != 0) { @@ -3964,6 +3984,7 @@ grub_zfs_open (struct grub_file *file, const char *fsfilename) err = zio_read (bp, data->dnode.endian, &sahdrp, NULL, data); if (err) return err; + free_sahdrp = true; } else { @@ -3972,6 +3993,8 @@ grub_zfs_open (struct grub_file *file, const char *fsfilename) hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp)); file->size = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_SIZE_OFFSET), data->dnode.endian); + if (free_sahdrp) + grub_free(sahdrp); } else if (data->dnode.dn.dn_bonustype == DMU_OT_ZNODE) { @@ -4157,6 +4180,7 @@ fill_fs_info (struct grub_dirhook_info *info, { void *sahdrp; int hdrsize; + bool free_sahdrp = false; if (dn.dn.dn_bonuslen != 0) { @@ -4169,6 +4193,7 @@ fill_fs_info (struct grub_dirhook_info *info, err = zio_read (bp, dn.endian, &sahdrp, NULL, data); if (err) return err; + free_sahdrp = true; } else { @@ -4179,6 +4204,8 @@ fill_fs_info (struct grub_dirhook_info *info, hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp)); info->mtimeset = 1; info->mtime = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_MTIME_OFFSET), dn.endian); + if (free_sahdrp) + grub_free (sahdrp); } if (dn.dn.dn_bonustype == DMU_OT_ZNODE) @@ -4210,6 +4237,7 @@ iterate_zap (const char *name, grub_uint64_t val, struct grub_zfs_dir_ctx *ctx) { void *sahdrp; int hdrsize; + bool free_sahdrp = false; if (dn.dn.dn_bonuslen != 0) { @@ -4225,6 +4253,7 @@ iterate_zap (const char *name, grub_uint64_t val, struct grub_zfs_dir_ctx *ctx) grub_print_error (); return 0; } + free_sahdrp = true; } else { @@ -4237,6 +4266,8 @@ iterate_zap (const char *name, grub_uint64_t val, struct grub_zfs_dir_ctx *ctx) info.mtimeset = 1; info.mtime = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_MTIME_OFFSET), dn.endian); info.case_insensitive = ctx->data->subvol.case_insensitive; + if (free_sahdrp) + grub_free (sahdrp); } if (dn.dn.dn_bonustype == DMU_OT_ZNODE) @@ -4448,7 +4479,7 @@ check_mos_features(dnode_phys_t *mosmdn_phys,grub_zfs_endian_t endian,struct gru dnode_end_t dn,mosmdn; mzap_phys_t* mzp; grub_zfs_endian_t endianzap; - int size; + int size, ret; grub_memmove(&(mosmdn.dn),mosmdn_phys,sizeof(dnode_phys_t)); mosmdn.endian=endian; errnum = dnode_get(&mosmdn, DMU_POOL_DIRECTORY_OBJECT, @@ -4474,7 +4505,9 @@ check_mos_features(dnode_phys_t *mosmdn_phys,grub_zfs_endian_t endian,struct gru return errnum; size = grub_zfs_to_cpu16 (dn.dn.dn_datablkszsec, dn.endian) << SPA_MINBLOCKSHIFT; - return mzap_iterate (mzp,endianzap, size, check_feature,NULL); + ret = mzap_iterate (mzp,endianzap, size, check_feature,NULL); + grub_free(mzp); + return ret; } diff --git a/grub-core/fs/zfs/zfsinfo.c b/grub-core/fs/zfs/zfsinfo.c index c8a28acf5..31d767bbd 100644 --- a/grub-core/fs/zfs/zfsinfo.c +++ b/grub-core/fs/zfs/zfsinfo.c @@ -379,14 +379,17 @@ grub_cmd_zfs_bootfs (grub_command_t cmd __attribute__ ((unused)), int argc, grub_device_close (dev); - if (err) + if (err) { + grub_free (nvlist); return err; + } poolname = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_POOL_NAME); if (!poolname) { if (!grub_errno) grub_error (GRUB_ERR_BAD_FS, "No poolname found"); + grub_free (nvlist); return grub_errno; } From fee6081ec79557d72b6d1849222441207db45588 Mon Sep 17 00:00:00 2001 From: Avnish Chouhan Date: Thu, 13 Mar 2025 19:45:50 +0530 Subject: [PATCH 368/402] kern/ieee1275/init: Increase MIN_RMA size for CAS negotiation on PowerPC machines Change RMA size from 512 MB to 768 MB which will result in more memory at boot time for PowerPC. When vTPM, Secure Boot or FADump are enabled on PowerPC the 512 MB RMA memory is not sufficient for boot. With this 512 MB RMA, GRUB runs out of memory and fails to boot the machine. Sometimes even usage of CDROM requires more memory for installation and along with the options mentioned above exhausts the boot memory which results in boot failures. Increasing the RMA size will resolves multiple out of memory issues observed on PowerPC machines. Failure details (GRUB debug console dump): kern/ieee1275/init.c:550: mm requested region of size 8513000, flags 1 kern/ieee1275/init.c:563: Cannot satisfy allocation and retain minimum runtime space kern/ieee1275/init.c:550: mm requested region of size 8513000, flags 0 kern/ieee1275/init.c:563: Cannot satisfy allocation and retain minimum runtime space kern/file.c:215: Closing `/ppc/ppc64/initrd.img' ... kern/disk.c:297: Closing `ieee1275//vdevice/v-scsi@30000067/disk@8300000000000000'... kern/disk.c:311: Closing `ieee1275//vdevice/v-scsi@30000067/disk@8300000000000000' succeeded. kern/file.c:225: Closing `/ppc/ppc64/initrd.img' failed with 3. kern/file.c:148: Opening `/ppc/ppc64/initrd.img' succeeded. error: ../../grub-core/kern/mm.c:552:out of memory. Signed-off-by: Avnish Chouhan Reviewed-by: Daniel Kiper --- grub-core/kern/ieee1275/init.c | 52 ++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index dfbd0b899..a5586f85b 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -780,7 +780,7 @@ struct cas_vector /* * Call ibm,client-architecture-support to try to get more RMA. - * We ask for 512MB which should be enough to verify a distro kernel. + * We ask for 768MB which should be enough to verify a distro kernel. * We ignore most errors: if we don't succeed we'll proceed with whatever * memory we have. */ @@ -852,7 +852,7 @@ grub_ieee1275_ibm_cas (void) .vec1 = 0x80, /* ignore */ .vec2_size = 1 + sizeof (struct option_vector2) - 2, .vec2 = { - 0, 0, -1, -1, -1, -1, -1, 512, -1, 0, 48 + 0, 0, -1, -1, -1, -1, -1, 768, -1, 0, 48 }, .vec3_size = 2 - 1, .vec3 = 0x00e0, /* ask for FP + VMX + DFP but don't halt if unsatisfied */ @@ -889,6 +889,10 @@ grub_claim_heap (void) { grub_err_t err; grub_uint32_t total = HEAP_MAX_SIZE; +#if defined(__powerpc__) + grub_uint32_t ibm_ca_support_reboot = 0; + grub_ssize_t actual; +#endif err = grub_ieee1275_total_mem (&rmo_top); @@ -901,11 +905,49 @@ grub_claim_heap (void) grub_mm_add_region_fn = grub_ieee1275_mm_add_region; #if defined(__powerpc__) + /* Check if it's a CAS reboot with below property. If so, we will skip CAS call. */ + if (grub_ieee1275_get_integer_property (grub_ieee1275_chosen, + "ibm,client-architecture-support-reboot", + &ibm_ca_support_reboot, + sizeof (ibm_ca_support_reboot), + &actual) >= 0) + grub_dprintf ("ieee1275", "ibm,client-architecture-support-reboot: %" PRIuGRUB_UINT32_T "\n", + ibm_ca_support_reboot); + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY)) { - /* if we have an error, don't call CAS, just hope for the best */ - if (err == GRUB_ERR_NONE && rmo_top < (512 * 1024 * 1024)) - grub_ieee1275_ibm_cas (); + /* + * If we have an error don't call CAS. Just hope for the best. + * Along with the above, if the rmo_top is 512 MB or above. We + * will skip the CAS call. However, if we call CAS, the rmo_top + * will be set to 768 MB via CAS Vector2. But we need to call + * CAS with rmo_top < 512 MB to avoid the issue on the older + * Linux kernel, which still uses rmo_top as 512 MB. If we call + * CAS with a condition rmo_top < 768 MB, it will result in an + * issue due to the IBM CAS reboot feature and we won't be able + * to boot the newer kernel. Whenever a reboot is detected as + * the CAS reboot by GRUB it will boot the machine with the + * last booted kernel by reading the variable boot-last-label + * which has the info related to the last boot and it's specific + * to IBM PowerPC. Due to this, the machine will boot with the + * last booted kernel which has rmo_top as 512 MB. Also, if the + * reboot is detected as a CAS reboot, the GRUB will skip the CAS + * call. As the CAS has already been called earlier, so it is + * not required to call CAS even if the other conditions are met. + * This condition will also prevent a scenario where the machine + * get stuck in the CAS reboot loop while booting a machine with + * an older kernel, having option_vector2 MIN_RMA as 512 MB in + * Linux prom_init.c and GRUB uses rmo_top < 768 MB condition + * for calling CAS. Due to their respective conditions, Linux + * CAS and GRUB CAS will keep doing the CAS calls and change + * the MIN_RMA from 768, changed by GRUB, to 512, changed by Linux, + * to 768, changed by GRUB, to 512, changed by Linux, and so on. + * The machine will stuck in this CAS reboot loop forever. + * + * IBM PAPR: https://openpower.foundation/specifications/linuxonpower/ + */ + if (!ibm_ca_support_reboot && err == GRUB_ERR_NONE && rmo_top < (512 * 1024 * 1024)) + grub_ieee1275_ibm_cas (); } #endif From 3b25e494d47e7a728e7ce6264b10f2aa1063f9c7 Mon Sep 17 00:00:00 2001 From: Nicolas Frayer Date: Wed, 19 Mar 2025 17:39:41 +0100 Subject: [PATCH 369/402] net/drivers/ieee1275/ofnet: Add missing grub_malloc() The grub_malloc() has been inadvertently removed from the code after it has been modified to use safe math functions. Fixes: 4beeff8a (net: Use safe math macros to prevent overflows) Signed-off-by: Nicolas Frayer Tested-by: Marta Lewandowska Reviewed-by: Daniel Kiper --- grub-core/net/drivers/ieee1275/ofnet.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/net/drivers/ieee1275/ofnet.c b/grub-core/net/drivers/ieee1275/ofnet.c index 3e1b9094e..e5be362a9 100644 --- a/grub-core/net/drivers/ieee1275/ofnet.c +++ b/grub-core/net/drivers/ieee1275/ofnet.c @@ -463,6 +463,9 @@ search_net_devices (struct grub_ieee1275_devalias *alias) return 0; } } + + ofdata->path = grub_malloc (sz); + if (!ofdata->path) { grub_print_error (); From aae2ea619e0f09c353fd6929910f925c92a412c0 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Thu, 20 Mar 2025 18:28:00 -0500 Subject: [PATCH 370/402] fs/ntfs: Fix NULL pointer dereference and possible infinite loop A regression was introduced recently as a part of the series of filesystem related patches to address some CVEs found in GRUB. This issue may cause either an infinite loop at startup when accessing certain valid NTFS filesystems, or may cause a crash due to a NULL pointer dereference on systems where NULL address is invalid (such as may happen when calling grub-mount from the operating system level). Correct this issue by checking that at->attr_cur is within bounds inside find_attr(). Fixes: https://savannah.gnu.org/bugs/?66855 Fixes: aff263187 (fs/ntfs: Fix out-of-bounds read) Signed-off-by: B Horn Signed-off-by: Andrew Hamilton Reviewed-by: Daniel Kiper --- grub-core/fs/ntfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index 960833a34..b3117bf92 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -387,7 +387,7 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) } at->attr_cur = at->attr_nxt; mft_end = at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR); - while (at->attr_cur < mft_end && *at->attr_cur != 0xFF) + while (at->attr_cur >= at->mft->buf && at->attr_cur < mft_end && *at->attr_cur != 0xFF) { at->attr_nxt = next_attribute (at->attr_cur, at->end); if (*at->attr_cur == GRUB_NTFS_AT_ATTRIBUTE_LIST) From f3f1fcecdce5e8d7099befc3d2c3ae25eb7cc954 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Thu, 27 Mar 2025 17:56:31 +0000 Subject: [PATCH 371/402] disk/ldm: Fix memory leaks Fix memory leaks in make_vg() with new helper functions, free_pv() and free_lv(). Additionally, correct a check after allocating comp->segments->nodes that mistakenly checked lv->segments->nodes instead, likely due to a copy-paste error. Fixes: CID 473878 Fixes: CID 473884 Fixes: CID 473889 Fixes: CID 473890 Signed-off-by: Lidong Chen Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/disk/ldm.c | 180 +++++++++++++++++++------------------------ 1 file changed, 80 insertions(+), 100 deletions(-) diff --git a/grub-core/disk/ldm.c b/grub-core/disk/ldm.c index 048e29cd0..0f4f22aaa 100644 --- a/grub-core/disk/ldm.c +++ b/grub-core/disk/ldm.c @@ -179,6 +179,36 @@ gpt_ldm_sector (grub_disk_t dsk) return sector; } +static void +free_pv (struct grub_diskfilter_pv *pv) +{ + if (pv == NULL) + return; + + grub_free (pv->internal_id); + grub_free (pv->id.uuid); + grub_free (pv); +} + +static void +free_lv (struct grub_diskfilter_lv *lv) +{ + if (lv == NULL) + return; + + grub_free (lv->internal_id); + grub_free (lv->name); + grub_free (lv->fullname); + if (lv->segments) + { + unsigned int i; + for (i = 0; i < lv->segment_count; i++) + grub_free (lv->segments[i].nodes); + grub_free (lv->segments); + } + grub_free (lv); +} + static struct grub_diskfilter_vg * make_vg (grub_disk_t disk, const struct grub_ldm_label *label) @@ -196,12 +226,8 @@ make_vg (grub_disk_t disk, vg->name = grub_malloc (LDM_NAME_STRLEN + 1); vg->uuid = grub_malloc (LDM_GUID_STRLEN + 1); if (! vg->uuid || !vg->name) - { - grub_free (vg->uuid); - grub_free (vg->name); - grub_free (vg); - return NULL; - } + goto fail1; + grub_memcpy (vg->uuid, label->group_guid, LDM_GUID_STRLEN); grub_memcpy (vg->name, label->group_name, LDM_NAME_STRLEN); vg->name[LDM_NAME_STRLEN] = 0; @@ -261,7 +287,7 @@ make_vg (grub_disk_t disk, pv->internal_id = grub_malloc (sz); if (!pv->internal_id) { - grub_free (pv); + free_pv (pv); goto fail2; } grub_memcpy (pv->internal_id, ptr, (grub_size_t) ptr[0] + 1); @@ -271,7 +297,7 @@ make_vg (grub_disk_t disk, if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { - grub_free (pv); + free_pv (pv); goto fail2; } /* ptr = name. */ @@ -279,23 +305,21 @@ make_vg (grub_disk_t disk, if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { - grub_free (pv); + free_pv (pv); goto fail2; } pv->id.uuidlen = *ptr; if (grub_add (pv->id.uuidlen, 1, &sz)) { - grub_free (pv->internal_id); - grub_free (pv); + free_pv (pv); goto fail2; } pv->id.uuid = grub_malloc (sz); if (pv->id.uuid == NULL) { - grub_free (pv->internal_id); - grub_free (pv); + free_pv (pv); goto fail2; } grub_memcpy (pv->id.uuid, ptr + 1, pv->id.uuidlen); @@ -343,7 +367,7 @@ make_vg (grub_disk_t disk, lv->segments = grub_zalloc (sizeof (*lv->segments)); if (!lv->segments) { - grub_free (lv); + free_lv (lv); goto fail2; } lv->segments->start_extent = 0; @@ -354,26 +378,25 @@ make_vg (grub_disk_t disk, sizeof (*lv->segments->nodes)); if (!lv->segments->nodes) { - grub_free (lv); + free_lv (lv); goto fail2; } ptr = vblk[i].dynamic; if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { - grub_free (lv); + free_lv (lv); goto fail2; } if (grub_add (ptr[0], 2, &sz)) { - grub_free (lv->segments); - grub_free (lv); + free_lv (lv); goto fail2; } lv->internal_id = grub_malloc (sz); if (!lv->internal_id) { - grub_free (lv); + free_lv (lv); goto fail2; } grub_memcpy (lv->internal_id, ptr, ptr[0] + 1); @@ -383,20 +406,18 @@ make_vg (grub_disk_t disk, if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { - grub_free (lv); + free_lv (lv); goto fail2; } if (grub_add (*ptr, 1, &sz)) { - grub_free (lv->internal_id); - grub_free (lv); + free_lv (lv); goto fail2; } lv->name = grub_malloc (sz); if (!lv->name) { - grub_free (lv->internal_id); - grub_free (lv); + free_lv (lv); goto fail2; } grub_memcpy (lv->name, ptr + 1, *ptr); @@ -405,36 +426,28 @@ make_vg (grub_disk_t disk, vg->uuid, lv->name); if (!lv->fullname) { - grub_free (lv->internal_id); - grub_free (lv->name); - grub_free (lv); + free_lv (lv); goto fail2; } ptr += *ptr + 1; if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { - grub_free (lv->internal_id); - grub_free (lv->name); - grub_free (lv); + free_lv (lv); goto fail2; } /* ptr = volume type. */ ptr += *ptr + 1; if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { - grub_free (lv->internal_id); - grub_free (lv->name); - grub_free (lv); + free_lv (lv); goto fail2; } /* ptr = flags. */ ptr += *ptr + 1; if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { - grub_free (lv->internal_id); - grub_free (lv->name); - grub_free (lv); + free_lv (lv); goto fail2; } @@ -443,17 +456,13 @@ make_vg (grub_disk_t disk, /* ptr = number of children. */ if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { - grub_free (lv->internal_id); - grub_free (lv->name); - grub_free (lv); + free_lv (lv); goto fail2; } ptr += *ptr + 1; if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { - grub_free (lv->internal_id); - grub_free (lv->name); - grub_free (lv); + free_lv (lv); goto fail2; } @@ -463,9 +472,7 @@ make_vg (grub_disk_t disk, || ptr + *ptr + 1>= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { - grub_free (lv->internal_id); - grub_free (lv->name); - grub_free (lv); + free_lv (lv); goto fail2; } lv->size = read_int (ptr + 1, *ptr); @@ -515,18 +522,18 @@ make_vg (grub_disk_t disk, ptr = vblk[i].dynamic; if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { - grub_free (comp); + free_lv (comp); goto fail2; } if (grub_add (ptr[0], 2, &sz)) { - grub_free (comp); + free_lv (comp); goto fail2; } comp->internal_id = grub_malloc (sz); if (!comp->internal_id) { - grub_free (comp); + free_lv (comp); goto fail2; } grub_memcpy (comp->internal_id, ptr, ptr[0] + 1); @@ -535,16 +542,14 @@ make_vg (grub_disk_t disk, ptr += *ptr + 1; if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { - grub_free (comp->internal_id); - grub_free (comp); + free_lv (comp); goto fail2; } /* ptr = name. */ ptr += *ptr + 1; if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { - grub_free (comp->internal_id); - grub_free (comp); + free_lv (comp); goto fail2; } /* ptr = state. */ @@ -554,8 +559,7 @@ make_vg (grub_disk_t disk, ptr += 4; if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { - grub_free (comp->internal_id); - grub_free (comp); + free_lv (comp); goto fail2; } @@ -563,16 +567,14 @@ make_vg (grub_disk_t disk, ptr += *ptr + 1; if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { - grub_free (comp->internal_id); - grub_free (comp); + free_lv (comp); goto fail2; } ptr += 8 + 8; if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { - grub_free (comp->internal_id); - grub_free (comp); + free_lv (comp); goto fail2; } for (lv = vg->lvs; lv; lv = lv->next) @@ -583,8 +585,7 @@ make_vg (grub_disk_t disk, } if (!lv) { - grub_free (comp->internal_id); - grub_free (comp); + free_lv (comp); continue; } comp->size = lv->size; @@ -596,8 +597,7 @@ make_vg (grub_disk_t disk, sizeof (*comp->segments)); if (!comp->segments) { - grub_free (comp->internal_id); - grub_free (comp); + free_lv (comp); goto fail2; } } @@ -608,8 +608,7 @@ make_vg (grub_disk_t disk, comp->segments = grub_malloc (sizeof (*comp->segments)); if (!comp->segments) { - grub_free (comp->internal_id); - grub_free (comp); + free_lv (comp); goto fail2; } comp->segments->start_extent = 0; @@ -624,27 +623,21 @@ make_vg (grub_disk_t disk, } else { - grub_free (comp->segments); - grub_free (comp->internal_id); - grub_free (comp); + free_lv (comp); goto fail2; } ptr += *ptr + 1; ptr++; if (!(vblk[i].flags & 0x10)) { - grub_free (comp->segments); - grub_free (comp->internal_id); - grub_free (comp); + free_lv (comp); goto fail2; } if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic) || ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { - grub_free (comp->segments); - grub_free (comp->internal_id); - grub_free (comp); + free_lv (comp); goto fail2; } comp->segments->stripe_size = read_int (ptr + 1, *ptr); @@ -652,20 +645,16 @@ make_vg (grub_disk_t disk, if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) { - grub_free (comp->segments); - grub_free (comp->internal_id); - grub_free (comp); + free_lv (comp); goto fail2; } comp->segments->node_count = read_int (ptr + 1, *ptr); comp->segments->node_alloc = comp->segments->node_count; comp->segments->nodes = grub_calloc (comp->segments->node_alloc, sizeof (*comp->segments->nodes)); - if (!lv->segments->nodes) + if (comp->segments->nodes == NULL) { - grub_free (comp->segments); - grub_free (comp->internal_id); - grub_free (comp); + free_lv (comp); goto fail2; } } @@ -677,20 +666,14 @@ make_vg (grub_disk_t disk, if (grub_mul (lv->segments->node_alloc, 2, &lv->segments->node_alloc) || grub_mul (lv->segments->node_alloc, sizeof (*lv->segments->nodes), &sz)) { - grub_free (comp->segments->nodes); - grub_free (comp->segments); - grub_free (comp->internal_id); - grub_free (comp); + free_lv (comp); goto fail2; } t = grub_realloc (lv->segments->nodes, sz); if (!t) { - grub_free (comp->segments->nodes); - grub_free (comp->segments); - grub_free (comp->internal_id); - grub_free (comp); + free_lv (comp); goto fail2; } lv->segments->nodes = t; @@ -830,7 +813,10 @@ make_vg (grub_disk_t disk, comp->segments[comp->segment_count].nodes = grub_malloc (sizeof (*comp->segments[comp->segment_count].nodes)); if (!comp->segments[comp->segment_count].nodes) - goto fail2; + { + grub_free (comp->segments); + goto fail2; + } comp->segments[comp->segment_count].nodes[0] = part; comp->segment_count++; } @@ -845,25 +831,19 @@ make_vg (grub_disk_t disk, struct grub_diskfilter_pv *pv, *next_pv; for (lv = vg->lvs; lv; lv = next_lv) { - unsigned i; - for (i = 0; i < lv->segment_count; i++) - grub_free (lv->segments[i].nodes); - next_lv = lv->next; - grub_free (lv->segments); - grub_free (lv->internal_id); - grub_free (lv->name); - grub_free (lv->fullname); - grub_free (lv); + free_lv (lv); + } for (pv = vg->pvs; pv; pv = next_pv) { next_pv = pv->next; - grub_free (pv->id.uuid); - grub_free (pv); + free_pv (pv); } } + fail1: grub_free (vg->uuid); + grub_free (vg->name); grub_free (vg); return NULL; } From 1d005944754b7298b6df3debddb6abae22745265 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Thu, 27 Mar 2025 17:56:32 +0000 Subject: [PATCH 372/402] lib/reloacator: Fix memory leaks Fix memory leaks in grub_relocator_alloc_chunk_align(). Fixes: CID 473844 Signed-off-by: Lidong Chen Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/lib/relocator.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/grub-core/lib/relocator.c b/grub-core/lib/relocator.c index e0478ae5b..3306a1bb7 100644 --- a/grub-core/lib/relocator.c +++ b/grub-core/lib/relocator.c @@ -1440,6 +1440,7 @@ grub_relocator_alloc_chunk_align (struct grub_relocator *rel, break; } + grub_free (ctx.chunk); return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); } while (0); @@ -1456,7 +1457,10 @@ grub_relocator_alloc_chunk_align (struct grub_relocator *rel, grub_mmap_iterate (grub_relocator_alloc_chunk_align_iter, &ctx); #endif if (!ctx.found) - return grub_error (GRUB_ERR_BAD_OS, "couldn't find suitable memory target"); + { + grub_free (ctx.chunk); + return grub_error (GRUB_ERR_BAD_OS, "couldn't find suitable memory target"); + } } while (1) { From 81146fb6238f5202fada90f0da49ba3580b944c2 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Thu, 27 Mar 2025 17:56:33 +0000 Subject: [PATCH 373/402] loader/i386/linux: Fix resource leak In grub_cmd_initrd(), initrd_ctx is allocated before calling grub_relocator_alloc_chunk_align(). When that function fails, initrd_ctx should be freed before exiting grub_cmd_initrd(). Fixes: CID 473852 Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper --- grub-core/loader/i386/linux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index b7c1e057e..4b26cd816 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -1128,7 +1128,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), GRUB_RELOCATOR_PREFERENCE_HIGH, 1); if (err) - return err; + goto fail; initrd_mem = get_virtual_current_address (ch); initrd_mem_target = get_physical_target_address (ch); } From f94d257e8c983e6c3f4a7f5ce8a45f5cd7299f04 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Thu, 27 Mar 2025 17:56:34 +0000 Subject: [PATCH 374/402] fs/btrfs: Fix memory leaks Fix memory leaks in grub_btrfs_extent_read() and grub_btrfs_dir(). Fixes: CID 473842 Fixes: CID 473871 Signed-off-by: Lidong Chen Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/fs/btrfs.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 9c1e925c9..7bf8d922f 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -1538,7 +1538,10 @@ grub_btrfs_extent_read (struct grub_btrfs_data *data, err = lower_bound (data, &key_in, &key_out, tree, &elemaddr, &elemsize, &desc, 0); if (err) - return -1; + { + grub_free (desc.data); + return -1; + } if (key_out.object_id != ino || key_out.type != GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM) { @@ -2115,6 +2118,7 @@ grub_btrfs_dir (grub_device_t device, const char *path, if (err) { grub_btrfs_unmount (data); + grub_free (desc.data); return err; } if (key_out.type != GRUB_BTRFS_ITEM_TYPE_DIR_ITEM From 6b64f297e5906feb295dcc6fb5046368f5f3e95b Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Thu, 27 Mar 2025 17:56:35 +0000 Subject: [PATCH 375/402] loader/xnu: Fix memory leak In grub_xnu_load_kext_from_dir(), when the call to grub_device_open() failed, it simply cleaned up previously allocated memory and returned GRUB_ERR_NONE. However, it neglected to free ctx->newdirname which is allocated before the call to grub_device_open(). Fixes: CID 473859 Signed-off-by: Lidong Chen Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/loader/xnu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/grub-core/loader/xnu.c b/grub-core/loader/xnu.c index 4cca55df9..80831386e 100644 --- a/grub-core/loader/xnu.c +++ b/grub-core/loader/xnu.c @@ -1258,6 +1258,7 @@ grub_xnu_load_kext_from_dir (char *dirname, const char *osbundlerequired, grub_device_close (dev); } grub_free (device_name); + grub_free (ctx.newdirname); return GRUB_ERR_NONE; } From 204a6ddfb3bb456929e7c364e1e8d6ee1928b471 Mon Sep 17 00:00:00 2001 From: Patrick Colp Date: Thu, 3 Apr 2025 14:04:11 -0700 Subject: [PATCH 376/402] loader/i386/linux: Update linux_kernel_params to match upstream Update linux_kernel_params to match the v6.13.7 upstream version of boot_params. Refactor most things out into structs, as the Linux kernel does. edid_info should be a struct with "unsigned char dummy[128]" and efi_info should be a struct as well, starting at 0x1c0. However, for backwards compatibility, GRUB can have efi_systab at 0x1b8 and padding at 0x1bc (or padding at both spots). This cuts into the end of edid_info. Make edid_info inline and only make it go up to 0x1b8. Signed-off-by: Patrick Colp Reviewed-by: Daniel Kiper --- grub-core/loader/i386/linux.c | 220 +++++++-------- include/grub/i386/linux.h | 512 ++++++++++++++++++++++------------ 2 files changed, 446 insertions(+), 286 deletions(-) diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index 4b26cd816..12731feb2 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -124,7 +124,7 @@ find_mmap_size (void) grub_mmap_iterate (count_hook, &count); - mmap_size = count * sizeof (struct grub_e820_mmap); + mmap_size = count * sizeof (struct grub_boot_e820_entry); /* Increase the size a bit for safety, because GRUB allocates more on later. */ @@ -212,20 +212,20 @@ allocate_pages (grub_size_t prot_size, grub_size_t *align, } static grub_err_t -grub_e820_add_region (struct grub_e820_mmap *e820_map, int *e820_num, +grub_e820_add_region (struct grub_boot_e820_entry *e820_entry, int *e820_num, grub_uint64_t start, grub_uint64_t size, grub_uint32_t type) { int n = *e820_num; - if ((n > 0) && (e820_map[n - 1].addr + e820_map[n - 1].size == start) && - (e820_map[n - 1].type == type)) - e820_map[n - 1].size += size; + if ((n > 0) && (e820_entry[n - 1].addr + e820_entry[n - 1].size == start) && + (e820_entry[n - 1].type == type)) + e820_entry[n - 1].size += size; else { - e820_map[n].addr = start; - e820_map[n].size = size; - e820_map[n].type = type; + e820_entry[n].addr = start; + e820_entry[n].size = size; + e820_entry[n].type = type; (*e820_num)++; } return GRUB_ERR_NONE; @@ -253,43 +253,43 @@ grub_linux_setup_video (struct linux_kernel_params *params) return 1; } - params->lfb_width = mode_info.width; - params->lfb_height = mode_info.height; - params->lfb_depth = mode_info.bpp; - params->lfb_line_len = mode_info.pitch; + params->screen_info.lfb_width = mode_info.width; + params->screen_info.lfb_height = mode_info.height; + params->screen_info.lfb_depth = mode_info.bpp; + params->screen_info.lfb_linelength = mode_info.pitch; - params->lfb_base = (grub_size_t) framebuffer; + params->screen_info.lfb_base = (grub_size_t) framebuffer; #if defined (GRUB_MACHINE_EFI) && defined (__x86_64__) - params->ext_lfb_base = (grub_size_t) (((grub_uint64_t)(grub_size_t) framebuffer) >> 32); - params->capabilities |= VIDEO_CAPABILITY_64BIT_BASE; + params->screen_info.ext_lfb_base = (grub_size_t) (((grub_uint64_t)(grub_size_t) framebuffer) >> 32); + params->screen_info.capabilities |= VIDEO_CAPABILITY_64BIT_BASE; #endif - params->lfb_size = ALIGN_UP (params->lfb_line_len * params->lfb_height, 65536); + params->screen_info.lfb_size = ALIGN_UP (params->screen_info.lfb_linelength * params->screen_info.lfb_height, 65536); - params->red_mask_size = mode_info.red_mask_size; - params->red_field_pos = mode_info.red_field_pos; - params->green_mask_size = mode_info.green_mask_size; - params->green_field_pos = mode_info.green_field_pos; - params->blue_mask_size = mode_info.blue_mask_size; - params->blue_field_pos = mode_info.blue_field_pos; - params->reserved_mask_size = mode_info.reserved_mask_size; - params->reserved_field_pos = mode_info.reserved_field_pos; + params->screen_info.red_size = mode_info.red_mask_size; + params->screen_info.red_pos = mode_info.red_field_pos; + params->screen_info.green_size = mode_info.green_mask_size; + params->screen_info.green_pos = mode_info.green_field_pos; + params->screen_info.blue_size = mode_info.blue_mask_size; + params->screen_info.blue_pos = mode_info.blue_field_pos; + params->screen_info.rsvd_size = mode_info.reserved_mask_size; + params->screen_info.rsvd_pos = mode_info.reserved_field_pos; if (gfxlfbvar && (gfxlfbvar[0] == '1' || gfxlfbvar[0] == 'y')) - params->have_vga = GRUB_VIDEO_LINUX_TYPE_SIMPLE; + params->screen_info.orig_video_isVGA = GRUB_VIDEO_LINUX_TYPE_SIMPLE; else { switch (driver_id) { case GRUB_VIDEO_DRIVER_VBE: - params->lfb_size >>= 16; - params->have_vga = GRUB_VIDEO_LINUX_TYPE_VESA; + params->screen_info.lfb_size >>= 16; + params->screen_info.orig_video_isVGA = GRUB_VIDEO_LINUX_TYPE_VESA; break; case GRUB_VIDEO_DRIVER_EFI_UGA: case GRUB_VIDEO_DRIVER_EFI_GOP: - params->have_vga = GRUB_VIDEO_LINUX_TYPE_EFIFB; + params->screen_info.orig_video_isVGA = GRUB_VIDEO_LINUX_TYPE_EFIFB; break; /* FIXME: check if better id is available. */ @@ -307,7 +307,7 @@ grub_linux_setup_video (struct linux_kernel_params *params) case GRUB_VIDEO_DRIVER_SDL: case GRUB_VIDEO_DRIVER_NONE: case GRUB_VIDEO_ADAPTER_CAPTURE: - params->have_vga = GRUB_VIDEO_LINUX_TYPE_SIMPLE; + params->screen_info.orig_video_isVGA = GRUB_VIDEO_LINUX_TYPE_SIMPLE; break; } } @@ -332,9 +332,9 @@ grub_linux_setup_video (struct linux_kernel_params *params) /* 6 is default after mode reset. */ width = 6; - params->red_mask_size = params->green_mask_size - = params->blue_mask_size = width; - params->reserved_mask_size = 0; + params->screen_info.red_size = params->screen_info.green_size + = params->screen_info.blue_size = width; + params->screen_info.rsvd_size = 0; } #endif @@ -391,7 +391,7 @@ grub_linux_boot_mmap_fill (grub_uint64_t addr, grub_uint64_t size, { struct grub_linux_boot_ctx *ctx = data; - if (grub_e820_add_region (ctx->params->e820_map, &ctx->e820_num, + if (grub_e820_add_region (ctx->params->e820_table, &ctx->e820_num, addr, size, type)) return 1; @@ -423,10 +423,10 @@ grub_linux_boot (void) "bootpath", bootpath, grub_strlen (bootpath) + 1, &len); - linux_params.ofw_signature = GRUB_LINUX_OFW_SIGNATURE; - linux_params.ofw_num_items = 1; - linux_params.ofw_cif_handler = (grub_uint32_t) grub_ieee1275_entry_fn; - linux_params.ofw_idt = 0; + linux_params.olpc_ofw_header.ofw_magic = GRUB_LINUX_OFW_SIGNATURE; + linux_params.olpc_ofw_header.ofw_version = 1; + linux_params.olpc_ofw_header.cif_handler = (grub_uint32_t) grub_ieee1275_entry_fn; + linux_params.olpc_ofw_header.irq_desc_table = 0; } #endif @@ -471,19 +471,19 @@ grub_linux_boot (void) if (grub_linux_setup_video (&linux_params)) { #if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_QEMU) - linux_params.have_vga = GRUB_VIDEO_LINUX_TYPE_TEXT; - linux_params.video_mode = 0x3; + linux_params.screen_info.orig_video_isVGA = GRUB_VIDEO_LINUX_TYPE_TEXT; + linux_params.screen_info.orig_video_mode = 0x3; #else - linux_params.have_vga = 0; - linux_params.video_mode = 0; - linux_params.video_width = 0; - linux_params.video_height = 0; + linux_params.screen_info.orig_video_isVGA = 0; + linux_params.screen_info.orig_video_mode = 0; + linux_params.screen_info.orig_video_cols = 0; + linux_params.screen_info.orig_video_lines = 0; #endif } #ifndef GRUB_MACHINE_IEEE1275 - if (linux_params.have_vga == GRUB_VIDEO_LINUX_TYPE_TEXT) + if (linux_params.screen_info.orig_video_isVGA == GRUB_VIDEO_LINUX_TYPE_TEXT) #endif { grub_term_output_t term; @@ -494,19 +494,19 @@ grub_linux_boot (void) || grub_strcmp (term->name, "ofconsole") == 0) { struct grub_term_coordinate pos = grub_term_getxy (term); - linux_params.video_cursor_x = pos.x; - linux_params.video_cursor_y = pos.y; - linux_params.video_width = grub_term_width (term); - linux_params.video_height = grub_term_height (term); + linux_params.screen_info.orig_x = pos.x; + linux_params.screen_info.orig_y = pos.y; + linux_params.screen_info.orig_video_cols = grub_term_width (term); + linux_params.screen_info.orig_video_lines = grub_term_height (term); found = 1; break; } if (!found) { - linux_params.video_cursor_x = 0; - linux_params.video_cursor_y = 0; - linux_params.video_width = 80; - linux_params.video_height = 25; + linux_params.screen_info.orig_x = 0; + linux_params.screen_info.orig_y = 0; + linux_params.screen_info.orig_video_cols = 80; + linux_params.screen_info.orig_video_lines = 25; } } @@ -517,8 +517,8 @@ grub_linux_boot (void) mmap_size = find_mmap_size (); /* Make sure that each size is aligned to a page boundary. */ cl_offset = ALIGN_UP (mmap_size + sizeof (linux_params), 4096); - if (cl_offset < ((grub_size_t) linux_params.setup_sects << GRUB_DISK_SECTOR_BITS)) - cl_offset = ALIGN_UP ((grub_size_t) (linux_params.setup_sects + if (cl_offset < ((grub_size_t) linux_params.hdr.setup_sects << GRUB_DISK_SECTOR_BITS)) + cl_offset = ALIGN_UP ((grub_size_t) (linux_params.hdr.setup_sects << GRUB_DISK_SECTOR_BITS), 4096); ctx.real_size = ALIGN_UP (cl_offset + maximal_cmdline_size, 4096); @@ -567,17 +567,17 @@ grub_linux_boot (void) ctx.params = real_mode_mem; *ctx.params = linux_params; - ctx.params->cmd_line_ptr = ctx.real_mode_target + cl_offset; + ctx.params->hdr.cmd_line_ptr = ctx.real_mode_target + cl_offset; grub_memcpy ((char *) ctx.params + cl_offset, linux_cmdline, maximal_cmdline_size); grub_dprintf ("linux", "code32_start = %x\n", - (unsigned) ctx.params->code32_start); + (unsigned) ctx.params->hdr.code32_start); ctx.e820_num = 0; if (grub_mmap_iterate (grub_linux_boot_mmap_fill, &ctx)) return grub_errno; - ctx.params->mmap_size = ctx.e820_num; + ctx.params->e820_entries = ctx.e820_num; #ifdef GRUB_MACHINE_EFI { @@ -596,42 +596,42 @@ grub_linux_boot (void) efi_mmap_target = ctx.real_mode_target + ((grub_uint8_t *) efi_mmap_buf - (grub_uint8_t *) real_mode_mem); /* Pass EFI parameters. */ - if (grub_le_to_cpu16 (ctx.params->version) >= 0x0208) + if (grub_le_to_cpu16 (ctx.params->hdr.version) >= 0x0208) { - ctx.params->v0208.efi_mem_desc_size = efi_desc_size; - ctx.params->v0208.efi_mem_desc_version = efi_desc_version; - ctx.params->v0208.efi_mmap = efi_mmap_target; - ctx.params->v0208.efi_mmap_size = efi_mmap_size; + ctx.params->efi_info.v0208.efi_memdesc_size = efi_desc_size; + ctx.params->efi_info.v0208.efi_memdesc_version = efi_desc_version; + ctx.params->efi_info.v0208.efi_memmap = efi_mmap_target; + ctx.params->efi_info.v0208.efi_memmap_size = efi_mmap_size; #ifdef __x86_64__ - ctx.params->v0208.efi_mmap_hi = (efi_mmap_target >> 32); + ctx.params->efi_info.v0208.efi_memmap_hi = (efi_mmap_target >> 32); #endif } - else if (grub_le_to_cpu16 (ctx.params->version) >= 0x0206) + else if (grub_le_to_cpu16 (ctx.params->hdr.version) >= 0x0206) { - ctx.params->v0206.efi_mem_desc_size = efi_desc_size; - ctx.params->v0206.efi_mem_desc_version = efi_desc_version; - ctx.params->v0206.efi_mmap = efi_mmap_target; - ctx.params->v0206.efi_mmap_size = efi_mmap_size; + ctx.params->efi_info.v0206.efi_memdesc_size = efi_desc_size; + ctx.params->efi_info.v0206.efi_memdesc_version = efi_desc_version; + ctx.params->efi_info.v0206.efi_memmap = efi_mmap_target; + ctx.params->efi_info.v0206.efi_memmap_size = efi_mmap_size; } - else if (grub_le_to_cpu16 (ctx.params->version) >= 0x0204) + else if (grub_le_to_cpu16 (ctx.params->hdr.version) >= 0x0204) { - ctx.params->v0204.efi_mem_desc_size = efi_desc_size; - ctx.params->v0204.efi_mem_desc_version = efi_desc_version; - ctx.params->v0204.efi_mmap = efi_mmap_target; - ctx.params->v0204.efi_mmap_size = efi_mmap_size; + ctx.params->efi_info.v0204.efi_memdesc_size = efi_desc_size; + ctx.params->efi_info.v0204.efi_memdesc_version = efi_desc_version; + ctx.params->efi_info.v0204.efi_memmap = efi_mmap_target; + ctx.params->efi_info.v0204.efi_memmap_size = efi_mmap_size; } } #endif #if defined (__x86_64__) && defined (GRUB_MACHINE_EFI) - if (grub_le_to_cpu16 (ctx.params->version) >= 0x020c && - (linux_params.xloadflags & LINUX_X86_XLF_KERNEL_64) != 0) + if (grub_le_to_cpu16 (ctx.params->hdr.version) >= 0x020c && + (linux_params.hdr.xloadflags & LINUX_X86_XLF_KERNEL_64) != 0) { struct grub_relocator64_efi_state state64; state64.rsi = ctx.real_mode_target; - state64.rip = ctx.params->code32_start + LINUX_X86_STARTUP64_OFFSET; + state64.rip = ctx.params->hdr.code32_start + LINUX_X86_STARTUP64_OFFSET; return grub_relocator64_efi_boot (relocator, state64); } #endif @@ -641,7 +641,7 @@ grub_linux_boot (void) state.ebp = state.edi = state.ebx = 0; state.esi = ctx.real_mode_target; state.esp = ctx.real_mode_target; - state.eip = ctx.params->code32_start; + state.eip = ctx.params->hdr.code32_start; return grub_relocator32_boot (relocator, state, 0); } @@ -791,7 +791,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } - grub_memcpy (&linux_params.setup_sects, &lh.setup_sects, len - 0x1F1); + grub_memcpy (&linux_params.hdr.setup_sects, &lh.setup_sects, len - 0x1F1); /* We've already read lh so there is no need to read it second time. */ len -= sizeof(lh); @@ -805,62 +805,62 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } - linux_params.code32_start = prot_mode_target + lh.code32_start - GRUB_LINUX_BZIMAGE_ADDR; - linux_params.kernel_alignment = ((grub_uint32_t) 1 << align); - linux_params.ps_mouse = linux_params.padding11 = 0; - linux_params.type_of_loader = GRUB_LINUX_BOOT_LOADER_TYPE; + linux_params.hdr.code32_start = prot_mode_target + lh.code32_start - GRUB_LINUX_BZIMAGE_ADDR; + linux_params.hdr.kernel_alignment = ((grub_uint32_t) 1 << align); + linux_params.hdr.boot_flag = 0; + linux_params.hdr.type_of_loader = GRUB_LINUX_BOOT_LOADER_TYPE; /* These two are used (instead of cmd_line_ptr) by older versions of Linux, and otherwise ignored. */ - linux_params.cl_magic = GRUB_LINUX_CL_MAGIC; - linux_params.cl_offset = 0x1000; + linux_params.screen_info.cl_magic = GRUB_LINUX_CL_MAGIC; + linux_params.screen_info.cl_offset = 0x1000; - linux_params.ramdisk_image = 0; - linux_params.ramdisk_size = 0; + linux_params.hdr.ramdisk_image = 0; + linux_params.hdr.ramdisk_size = 0; - linux_params.heap_end_ptr = GRUB_LINUX_HEAP_END_OFFSET; - linux_params.loadflags |= GRUB_LINUX_FLAG_CAN_USE_HEAP; + linux_params.hdr.heap_end_ptr = GRUB_LINUX_HEAP_END_OFFSET; + linux_params.hdr.loadflags |= GRUB_LINUX_FLAG_CAN_USE_HEAP; /* These are not needed to be precise, because Linux uses these values only to raise an error when the decompression code cannot find good space. */ - linux_params.ext_mem = ((32 * 0x100000) >> 10); - linux_params.alt_mem = ((32 * 0x100000) >> 10); + linux_params.screen_info.ext_mem_k = ((32 * 0x100000) >> 10); + linux_params.alt_mem_k = ((32 * 0x100000) >> 10); /* Ignored by Linux. */ - linux_params.video_page = 0; + linux_params.screen_info.orig_video_page = 0; /* Only used when `video_mode == 0x7', otherwise ignored. */ - linux_params.video_ega_bx = 0; + linux_params.screen_info.orig_video_ega_bx = 0; - linux_params.font_size = 16; /* XXX */ + linux_params.screen_info.orig_video_points = 16; /* XXX */ #ifdef GRUB_MACHINE_EFI #ifdef __x86_64__ - if (grub_le_to_cpu16 (linux_params.version) < 0x0208 && + if (grub_le_to_cpu16 (linux_params.hdr.version) < 0x0208 && ((grub_addr_t) grub_efi_system_table >> 32) != 0) { grub_errno = grub_error (GRUB_ERR_BAD_OS, "kernel does not support 64-bit addressing"); goto fail; } #endif - if (grub_le_to_cpu16 (linux_params.version) >= 0x0208) + if (grub_le_to_cpu16 (linux_params.hdr.version) >= 0x0208) { - linux_params.v0208.efi_signature = GRUB_LINUX_EFI_SIGNATURE; - linux_params.v0208.efi_system_table = (grub_uint32_t) (grub_addr_t) grub_efi_system_table; + linux_params.efi_info.v0208.efi_loader_signature = GRUB_LINUX_EFI_SIGNATURE; + linux_params.efi_info.v0208.efi_systab = (grub_uint32_t) (grub_addr_t) grub_efi_system_table; #ifdef __x86_64__ - linux_params.v0208.efi_system_table_hi = (grub_uint32_t) ((grub_uint64_t) grub_efi_system_table >> 32); + linux_params.efi_info.v0208.efi_systab_hi = (grub_uint32_t) ((grub_uint64_t) grub_efi_system_table >> 32); #endif } - else if (grub_le_to_cpu16 (linux_params.version) >= 0x0206) + else if (grub_le_to_cpu16 (linux_params.hdr.version) >= 0x0206) { - linux_params.v0206.efi_signature = GRUB_LINUX_EFI_SIGNATURE; - linux_params.v0206.efi_system_table = (grub_uint32_t) (grub_addr_t) grub_efi_system_table; + linux_params.efi_info.v0206.efi_loader_signature = GRUB_LINUX_EFI_SIGNATURE; + linux_params.efi_info.v0206.efi_systab = (grub_uint32_t) (grub_addr_t) grub_efi_system_table; } - else if (grub_le_to_cpu16 (linux_params.version) >= 0x0204) + else if (grub_le_to_cpu16 (linux_params.hdr.version) >= 0x0204) { - linux_params.v0204.efi_signature = GRUB_LINUX_EFI_SIGNATURE_0204; - linux_params.v0204.efi_system_table = (grub_uint32_t) (grub_addr_t) grub_efi_system_table; + linux_params.efi_info.v0204.efi_loader_signature = GRUB_LINUX_EFI_SIGNATURE_0204; + linux_params.efi_info.v0204.efi_systab = (grub_uint32_t) (grub_addr_t) grub_efi_system_table; } #endif @@ -999,7 +999,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), } else if (grub_memcmp (argv[i], "quiet", sizeof ("quiet") - 1) == 0) { - linux_params.loadflags |= GRUB_LINUX_FLAG_QUIET; + linux_params.hdr.loadflags |= GRUB_LINUX_FLAG_QUIET; } /* Create kernel command line. */ @@ -1074,9 +1074,9 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), aligned_size = ALIGN_UP (size, 4096); /* Get the highest address available for the initrd. */ - if (grub_le_to_cpu16 (linux_params.version) >= 0x0203) + if (grub_le_to_cpu16 (linux_params.hdr.version) >= 0x0203) { - addr_max = grub_cpu_to_le32 (linux_params.initrd_addr_max); + addr_max = grub_cpu_to_le32 (linux_params.hdr.initrd_addr_max); /* XXX in reality, Linux specifies a bogus value, so it is necessary to make sure that ADDR_MAX does not exceed @@ -1139,9 +1139,9 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), grub_dprintf ("linux", "Initrd (%p) at 0x%" PRIxGRUB_ADDR ", size=0x%" PRIxGRUB_SIZE "\n", initrd_mem, initrd_mem_target, size); - linux_params.ramdisk_image = initrd_mem_target; - linux_params.ramdisk_size = size; - linux_params.root_dev = 0x0100; /* XXX */ + linux_params.hdr.ramdisk_image = initrd_mem_target; + linux_params.hdr.ramdisk_size = size; + linux_params.hdr.root_dev = 0x0100; /* XXX */ fail: grub_initrd_close (&initrd_ctx); diff --git a/include/grub/i386/linux.h b/include/grub/i386/linux.h index d4b550869..6e0a8e5be 100644 --- a/include/grub/i386/linux.h +++ b/include/grub/i386/linux.h @@ -51,6 +51,9 @@ /* Maximum number of MBR signatures to store. */ #define EDD_MBR_SIG_MAX 16 +/* Number of edd_info structs starting at EDDBUF. */ +#define EDDMAXNR 6 + #ifdef __x86_64__ #define GRUB_LINUX_EFI_SIGNATURE \ @@ -87,22 +90,273 @@ #define GRUB_E820_NVS 4 #define GRUB_E820_BADRAM 5 -struct grub_e820_mmap +#define GRUB_E820_MAX_ENTRIES_ZEROPAGE 128 + +struct grub_screen_info +{ + grub_uint8_t orig_x; /* 0x00 */ + grub_uint8_t orig_y; /* 0x01 */ + grub_uint16_t ext_mem_k; /* 0x02 */ + grub_uint16_t orig_video_page; /* 0x04 */ + grub_uint8_t orig_video_mode; /* 0x06 */ + grub_uint8_t orig_video_cols; /* 0x07 */ + grub_uint8_t flags; /* 0x08 */ + grub_uint8_t unused2; /* 0x09 */ + grub_uint16_t orig_video_ega_bx; /* 0x0a */ + grub_uint16_t unused3; /* 0x0c */ + grub_uint8_t orig_video_lines; /* 0x0e */ + grub_uint8_t orig_video_isVGA; /* 0x0f */ + grub_uint16_t orig_video_points; /* 0x10 */ + + /* VESA graphic mode -- linear frame buffer */ + grub_uint16_t lfb_width; /* 0x12 */ + grub_uint16_t lfb_height; /* 0x14 */ + grub_uint16_t lfb_depth; /* 0x16 */ + grub_uint32_t lfb_base; /* 0x18 */ + grub_uint32_t lfb_size; /* 0x1c */ + grub_uint16_t cl_magic, cl_offset; /* 0x20 */ + grub_uint16_t lfb_linelength; /* 0x24 */ + grub_uint8_t red_size; /* 0x26 */ + grub_uint8_t red_pos; /* 0x27 */ + grub_uint8_t green_size; /* 0x28 */ + grub_uint8_t green_pos; /* 0x29 */ + grub_uint8_t blue_size; /* 0x2a */ + grub_uint8_t blue_pos; /* 0x2b */ + grub_uint8_t rsvd_size; /* 0x2c */ + grub_uint8_t rsvd_pos; /* 0x2d */ + grub_uint16_t vesapm_seg; /* 0x2e */ + grub_uint16_t vesapm_off; /* 0x30 */ + grub_uint16_t pages; /* 0x32 */ + grub_uint16_t vesa_attributes; /* 0x34 */ + grub_uint32_t capabilities; /* 0x36 */ + grub_uint32_t ext_lfb_base; /* 0x3a */ + grub_uint8_t _reserved[2]; /* 0x3e */ +} GRUB_PACKED; + +struct grub_apm_bios_info +{ + grub_uint16_t version; + grub_uint16_t cseg; + grub_uint32_t offset; + grub_uint16_t cseg_16; + grub_uint16_t dseg; + grub_uint16_t flags; + grub_uint16_t cseg_len; + grub_uint16_t cseg_16_len; + grub_uint16_t dseg_len; +}; + +struct grub_ist_info +{ + grub_uint32_t signature; + grub_uint32_t command; + grub_uint32_t event; + grub_uint32_t perf_level; +}; + +struct grub_sys_desc_table +{ + grub_uint16_t length; + grub_uint8_t table[14]; +}; + +struct grub_olpc_ofw_header { + grub_uint32_t ofw_magic; /* OFW signature */ + grub_uint32_t ofw_version; + grub_uint32_t cif_handler; /* callback into OFW */ + grub_uint32_t irq_desc_table; +} GRUB_PACKED; + +struct grub_setup_header +{ + grub_uint8_t setup_sects; /* The size of the setup in sectors */ + grub_uint16_t root_flags; /* If the root is mounted readonly */ + grub_uint32_t syssize; /* obsolete */ + grub_uint16_t ram_size; /* obsolete */ + grub_uint16_t vid_mode; /* Video mode control */ + grub_uint16_t root_dev; /* Default root device number */ + grub_uint16_t boot_flag; /* 1fe */ + grub_uint16_t jump; /* Jump instruction */ + grub_uint32_t header; /* Magic signature "HdrS" */ + grub_uint16_t version; /* Boot protocol version supported */ + grub_uint32_t realmode_swtch; /* Boot loader hook */ + grub_uint16_t start_sys; /* The load-low segment (obsolete) */ + grub_uint16_t kernel_version; /* Points to kernel version string */ + grub_uint8_t type_of_loader; /* Boot loader identifier */ + grub_uint8_t loadflags; /* Boot protocol option flags */ + grub_uint16_t setup_move_size; /* Move to high memory size */ + grub_uint32_t code32_start; /* Boot loader hook */ + grub_uint32_t ramdisk_image; /* initrd load address */ + grub_uint32_t ramdisk_size; /* initrd size */ + grub_uint32_t bootsect_kludge; /* obsolete */ + grub_uint16_t heap_end_ptr; /* Free memory after setup end */ + grub_uint8_t ext_loader_ver; /* Extended loader version */ + grub_uint8_t ext_loader_type; /* Extended loader type */ + grub_uint32_t cmd_line_ptr; /* Points to the kernel command line */ + grub_uint32_t initrd_addr_max; /* Maximum initrd address */ + grub_uint32_t kernel_alignment; /* Alignment of the kernel */ + grub_uint8_t relocatable_kernel; /* Is the kernel relocatable */ + grub_uint8_t min_alignment; + grub_uint16_t xloadflags; + grub_uint32_t cmdline_size; /* Size of the kernel command line */ + grub_uint32_t hardware_subarch; + grub_uint64_t hardware_subarch_data; + grub_uint32_t payload_offset; + grub_uint32_t payload_length; + grub_uint64_t setup_data; + grub_uint64_t pref_address; + grub_uint32_t init_size; + grub_uint32_t handover_offset; + grub_uint32_t kernel_info_offset; +} GRUB_PACKED; + +struct grub_boot_e820_entry { grub_uint64_t addr; grub_uint64_t size; grub_uint32_t type; } GRUB_PACKED; +struct grub_edd_device_params +{ + grub_uint16_t length; + grub_uint16_t info_flags; + grub_uint32_t num_default_cylinders; + grub_uint32_t num_default_heads; + grub_uint32_t sectors_per_track; + grub_uint64_t number_of_sectors; + grub_uint16_t bytes_per_sector; + grub_uint32_t dpte_ptr; /* 0xFFFFFFFF for our purposes */ + grub_uint16_t key; /* = 0xBEDD */ + grub_uint8_t device_path_info_length; /* = 44 */ + grub_uint8_t reserved2; + grub_uint16_t reserved3; + grub_uint8_t host_bus_type[4]; + grub_uint8_t interface_type[8]; + union + { + struct + { + grub_uint16_t base_address; + grub_uint16_t reserved1; + grub_uint32_t reserved2; + } isa; + struct + { + grub_uint8_t bus; + grub_uint8_t slot; + grub_uint8_t function; + grub_uint8_t channel; + grub_uint32_t reserved; + } pci; + /* pcix is same as pci */ + struct + { + grub_uint64_t reserved; + } ibnd; + struct + { + grub_uint64_t reserved; + } xprs; + struct + { + grub_uint64_t reserved; + } htpt; + struct + { + grub_uint64_t reserved; + } unknown; + } interface_path; + union + { + struct + { + grub_uint8_t device; + grub_uint8_t reserved1; + grub_uint16_t reserved2; + grub_uint32_t reserved3; + grub_uint64_t reserved4; + } ata; + struct + { + grub_uint8_t device; + grub_uint8_t lun; + grub_uint8_t reserved1; + grub_uint8_t reserved2; + grub_uint32_t reserved3; + grub_uint64_t reserved4; + } atapi; + struct + { + grub_uint16_t id; + grub_uint64_t lun; + grub_uint16_t reserved1; + grub_uint32_t reserved2; + } scsi; + struct + { + grub_uint64_t serial_number; + grub_uint64_t reserved; + } usb; + struct + { + grub_uint64_t eui; + grub_uint64_t reserved; + } i1394; + struct + { + grub_uint64_t wwid; + grub_uint64_t lun; + } fibre; + struct + { + grub_uint64_t identity_tag; + grub_uint64_t reserved; + } i2o; + struct + { + grub_uint32_t array_number; + grub_uint32_t reserved1; + grub_uint64_t reserved2; + } raid; + struct + { + grub_uint8_t device; + grub_uint8_t reserved1; + grub_uint16_t reserved2; + grub_uint32_t reserved3; + grub_uint64_t reserved4; + } sata; + struct + { + grub_uint64_t reserved1; + grub_uint64_t reserved2; + } unknown; + } device_path; + grub_uint8_t reserved4; + grub_uint8_t checksum; +} GRUB_PACKED; + +struct grub_edd_info +{ + grub_uint8_t device; + grub_uint8_t version; + grub_uint16_t interface_support; + grub_uint16_t legacy_max_cylinder; + grub_uint8_t legacy_max_head; + grub_uint8_t legacy_sectors_per_track; + struct grub_edd_device_params params; +} GRUB_PACKED; + enum { GRUB_VIDEO_LINUX_TYPE_TEXT = 0x01, - GRUB_VIDEO_LINUX_TYPE_VESA = 0x23, /* VESA VGA in graphic mode. */ - GRUB_VIDEO_LINUX_TYPE_EFIFB = 0x70, /* EFI Framebuffer. */ - GRUB_VIDEO_LINUX_TYPE_SIMPLE = 0x70 /* Linear framebuffer without any additional functions. */ + GRUB_VIDEO_LINUX_TYPE_VESA = 0x23, /* VESA VGA in graphic mode. */ + GRUB_VIDEO_LINUX_TYPE_EFIFB = 0x70, /* EFI Framebuffer. */ + GRUB_VIDEO_LINUX_TYPE_SIMPLE = 0x70 /* Linear framebuffer without any additional functions. */ }; -/* For the Linux/i386 boot protocol version 2.10. */ +/* For the Linux/i386 boot protocol version 2.10. */ struct linux_i386_kernel_header { grub_uint8_t code1[0x0020]; @@ -144,7 +398,7 @@ struct linux_i386_kernel_header grub_uint16_t heap_end_ptr; /* Free memory after setup end */ grub_uint16_t pad1; /* Unused */ grub_uint32_t cmd_line_ptr; /* Points to the kernel command line */ - grub_uint32_t initrd_addr_max; /* Highest address for initrd */ + grub_uint32_t initrd_addr_max; /* Highest address for initrd */ grub_uint32_t kernel_alignment; grub_uint8_t relocatable; grub_uint8_t min_alignment; @@ -160,187 +414,93 @@ struct linux_i386_kernel_header grub_uint32_t handover_offset; } GRUB_PACKED; -/* Boot parameters for Linux based on 2.6.12. This is used by the setup - sectors of Linux, and must be simulated by GRUB on EFI, because - the setup sectors depend on BIOS. */ +/* + * Boot parameters for Linux based on 6.13.7 stable. This is used + * by the setup sectors of Linux, and must be simulated by GRUB + * on EFI, because the setup sectors depend on BIOS. + */ struct linux_kernel_params { - grub_uint8_t video_cursor_x; /* 0 */ - grub_uint8_t video_cursor_y; - - grub_uint16_t ext_mem; /* 2 */ - - grub_uint16_t video_page; /* 4 */ - grub_uint8_t video_mode; /* 6 */ - grub_uint8_t video_width; /* 7 */ - - grub_uint8_t padding1[0xa - 0x8]; - - grub_uint16_t video_ega_bx; /* a */ - - grub_uint8_t padding2[0xe - 0xc]; - - grub_uint8_t video_height; /* e */ - grub_uint8_t have_vga; /* f */ - grub_uint16_t font_size; /* 10 */ - - grub_uint16_t lfb_width; /* 12 */ - grub_uint16_t lfb_height; /* 14 */ - grub_uint16_t lfb_depth; /* 16 */ - grub_uint32_t lfb_base; /* 18 */ - grub_uint32_t lfb_size; /* 1c */ - - grub_uint16_t cl_magic; /* 20 */ - grub_uint16_t cl_offset; - - grub_uint16_t lfb_line_len; /* 24 */ - grub_uint8_t red_mask_size; /* 26 */ - grub_uint8_t red_field_pos; - grub_uint8_t green_mask_size; - grub_uint8_t green_field_pos; - grub_uint8_t blue_mask_size; - grub_uint8_t blue_field_pos; - grub_uint8_t reserved_mask_size; - grub_uint8_t reserved_field_pos; - grub_uint16_t vesapm_segment; /* 2e */ - grub_uint16_t vesapm_offset; /* 30 */ - grub_uint16_t lfb_pages; /* 32 */ - grub_uint16_t vesa_attrib; /* 34 */ - grub_uint32_t capabilities; /* 36 */ - grub_uint32_t ext_lfb_base; /* 3a */ - - grub_uint8_t padding3[0x40 - 0x3e]; - - grub_uint16_t apm_version; /* 40 */ - grub_uint16_t apm_code_segment; /* 42 */ - grub_uint32_t apm_entry; /* 44 */ - grub_uint16_t apm_16bit_code_segment; /* 48 */ - grub_uint16_t apm_data_segment; /* 4a */ - grub_uint16_t apm_flags; /* 4c */ - grub_uint32_t apm_code_len; /* 4e */ - grub_uint16_t apm_data_len; /* 52 */ - - grub_uint8_t padding4[0x60 - 0x54]; - - grub_uint32_t ist_signature; /* 60 */ - grub_uint32_t ist_command; /* 64 */ - grub_uint32_t ist_event; /* 68 */ - grub_uint32_t ist_perf_level; /* 6c */ - grub_uint64_t acpi_rsdp_addr; /* 70 */ - - grub_uint8_t padding5[0x80 - 0x78]; - - grub_uint8_t hd0_drive_info[0x10]; /* 80 */ - grub_uint8_t hd1_drive_info[0x10]; /* 90 */ - grub_uint16_t rom_config_len; /* a0 */ - - grub_uint8_t padding6[0xb0 - 0xa2]; - - grub_uint32_t ofw_signature; /* b0 */ - grub_uint32_t ofw_num_items; /* b4 */ - grub_uint32_t ofw_cif_handler; /* b8 */ - grub_uint32_t ofw_idt; /* bc */ - - grub_uint8_t padding7[0x1b8 - 0xc0]; + struct grub_screen_info screen_info; /* 0 */ + struct grub_apm_bios_info apm_bios_info; /* 40 */ + grub_uint8_t _pad2[4]; /* 54 */ + grub_uint64_t tboot_addr; /* 58 */ + struct grub_ist_info ist_info; /* 60 */ + grub_uint64_t acpi_rsdp_addr; /* 70 */ + grub_uint8_t _pad3[8]; /* 78 */ + grub_uint8_t hd0_info[16]; /* 80 */ + grub_uint8_t hd1_info[16]; /* 90 */ + struct grub_sys_desc_table sys_desc_table; /* a0 */ + struct grub_olpc_ofw_header olpc_ofw_header; /* b0 */ + grub_uint32_t ext_ramdisk_image; /* c0 */ + grub_uint32_t ext_ramdisk_size; /* c4 */ + grub_uint32_t ext_cmd_line_ptr; /* c8 */ + grub_uint8_t _pad4[112]; /* cc */ + grub_uint32_t cc_blob_address; /* 13c */ + /* + * edid_info should be a struct with "unsigned char dummy[128]" and + * efi_info should be a struct as well, starting at 0x1c0. However, + * for backwards compatibility, GRUB can have efi_systab at 0x1b8 and + * padding at 0x1bc (or padding at both spots). This cuts into the end + * of edid_info. Make edid_info inline and only make it go up to 0x1b8. + */ + grub_uint8_t edid_info[0x1b8 - 0x140]; /* 140 */ union { struct - { - grub_uint32_t efi_system_table; /* 1b8 */ - grub_uint32_t padding7_1; /* 1bc */ - grub_uint32_t efi_signature; /* 1c0 */ - grub_uint32_t efi_mem_desc_size; /* 1c4 */ - grub_uint32_t efi_mem_desc_version; /* 1c8 */ - grub_uint32_t efi_mmap_size; /* 1cc */ - grub_uint32_t efi_mmap; /* 1d0 */ - } v0204; + { + grub_uint32_t efi_systab; /* 1b8 */ + grub_uint32_t padding7_2; /* 1bc */ + grub_uint32_t efi_loader_signature; /* 1c0 */ + grub_uint32_t efi_memdesc_size; /* 1c4 */ + grub_uint32_t efi_memdesc_version; /* 1c8 */ + grub_uint32_t efi_memmap_size; /* 1cc */ + grub_uint32_t efi_memmap; /* 1d0 */ + } v0204; struct - { - grub_uint32_t padding7_1; /* 1b8 */ - grub_uint32_t padding7_2; /* 1bc */ - grub_uint32_t efi_signature; /* 1c0 */ - grub_uint32_t efi_system_table; /* 1c4 */ - grub_uint32_t efi_mem_desc_size; /* 1c8 */ - grub_uint32_t efi_mem_desc_version; /* 1cc */ - grub_uint32_t efi_mmap; /* 1d0 */ - grub_uint32_t efi_mmap_size; /* 1d4 */ + { + grub_uint32_t padding7_1; /* 1b8 */ + grub_uint32_t padding7_2; /* 1bc */ + grub_uint32_t efi_loader_signature; /* 1c0 */ + grub_uint32_t efi_systab; /* 1c4 */ + grub_uint32_t efi_memdesc_size; /* 1c8 */ + grub_uint32_t efi_memdesc_version; /* 1cc */ + grub_uint32_t efi_memmap; /* 1d0 */ + grub_uint32_t efi_memmap_size; /* 1d4 */ } v0206; struct - { - grub_uint32_t padding7_1; /* 1b8 */ - grub_uint32_t padding7_2; /* 1bc */ - grub_uint32_t efi_signature; /* 1c0 */ - grub_uint32_t efi_system_table; /* 1c4 */ - grub_uint32_t efi_mem_desc_size; /* 1c8 */ - grub_uint32_t efi_mem_desc_version; /* 1cc */ - grub_uint32_t efi_mmap; /* 1d0 */ - grub_uint32_t efi_mmap_size; /* 1d4 */ - grub_uint32_t efi_system_table_hi; /* 1d8 */ - grub_uint32_t efi_mmap_hi; /* 1dc */ - } v0208; - }; + { + grub_uint32_t padding7_1; /* 1b8 */ + grub_uint32_t padding7_2; /* 1bc */ + grub_uint32_t efi_loader_signature; /* 1c0 */ + grub_uint32_t efi_systab; /* 1c4 */ + grub_uint32_t efi_memdesc_size; /* 1c8 */ + grub_uint32_t efi_memdesc_version; /* 1cc */ + grub_uint32_t efi_memmap; /* 1d0 */ + grub_uint32_t efi_memmap_size; /* 1d4 */ + grub_uint32_t efi_systab_hi; /* 1d8 */ + grub_uint32_t efi_memmap_hi; /* 1dc */ + } v0208; + } efi_info; - grub_uint32_t alt_mem; /* 1e0 */ - - grub_uint8_t padding8[0x1e8 - 0x1e4]; - - grub_uint8_t mmap_size; /* 1e8 */ - - grub_uint8_t padding9[0x1ec - 0x1e9]; - - grub_uint8_t secure_boot; /* 1ec */ - - grub_uint8_t padding10[0x1f1 - 0x1ed]; - - /* Linux setup header copy - BEGIN. */ - grub_uint8_t setup_sects; /* The size of the setup in sectors */ - grub_uint16_t root_flags; /* If the root is mounted readonly */ - grub_uint16_t syssize; /* obsolete */ - grub_uint16_t swap_dev; /* obsolete */ - grub_uint16_t ram_size; /* obsolete */ - grub_uint16_t vid_mode; /* Video mode control */ - grub_uint16_t root_dev; /* Default root device number */ - - grub_uint8_t padding11; /* 1fe */ - grub_uint8_t ps_mouse; /* 1ff */ - - grub_uint16_t jump; /* Jump instruction */ - grub_uint32_t header; /* Magic signature "HdrS" */ - grub_uint16_t version; /* Boot protocol version supported */ - grub_uint32_t realmode_swtch; /* Boot loader hook */ - grub_uint16_t start_sys; /* The load-low segment (obsolete) */ - grub_uint16_t kernel_version; /* Points to kernel version string */ - grub_uint8_t type_of_loader; /* Boot loader identifier */ - grub_uint8_t loadflags; /* Boot protocol option flags */ - grub_uint16_t setup_move_size; /* Move to high memory size */ - grub_uint32_t code32_start; /* Boot loader hook */ - grub_uint32_t ramdisk_image; /* initrd load address */ - grub_uint32_t ramdisk_size; /* initrd size */ - grub_uint32_t bootsect_kludge; /* obsolete */ - grub_uint16_t heap_end_ptr; /* Free memory after setup end */ - grub_uint8_t ext_loader_ver; /* Extended loader version */ - grub_uint8_t ext_loader_type; /* Extended loader type */ - grub_uint32_t cmd_line_ptr; /* Points to the kernel command line */ - grub_uint32_t initrd_addr_max; /* Maximum initrd address */ - grub_uint32_t kernel_alignment; /* Alignment of the kernel */ - grub_uint8_t relocatable_kernel; /* Is the kernel relocatable */ - grub_uint8_t min_alignment; - grub_uint16_t xloadflags; - grub_uint32_t cmdline_size; /* Size of the kernel command line */ - grub_uint32_t hardware_subarch; - grub_uint64_t hardware_subarch_data; - grub_uint32_t payload_offset; - grub_uint32_t payload_length; - grub_uint64_t setup_data; - grub_uint64_t pref_address; - grub_uint32_t init_size; - grub_uint32_t handover_offset; - /* Linux setup header copy - END. */ - - grub_uint8_t _pad7[40]; + grub_uint32_t alt_mem_k; /* 1e0 */ + grub_uint32_t scratch; /* 1e4 */ + grub_uint8_t e820_entries; /* 1e8 */ + grub_uint8_t eddbuf_entries; /* 1e9 */ + grub_uint8_t edd_mbr_sig_buf_entries; /* 1ea */ + grub_uint8_t kbd_status; /* 1eb */ + grub_uint8_t secure_boot; /* 1ec */ + grub_uint8_t _pad5[2]; /* 1ed */ + grub_uint8_t sentinel; /* 1ef */ + grub_uint8_t _pad6[1]; /* 1f0 */ + struct grub_setup_header hdr; /* 1f1 */ + grub_uint8_t _pad7[0x290 - 0x1f1 - sizeof(struct grub_setup_header)]; grub_uint32_t edd_mbr_sig_buffer[EDD_MBR_SIG_MAX]; /* 290 */ - struct grub_e820_mmap e820_map[(0x400 - 0x2d0) / 20]; /* 2d0 */ + struct grub_boot_e820_entry e820_table[GRUB_E820_MAX_ENTRIES_ZEROPAGE]; /* 2d0 */ + grub_uint8_t _pad8[48]; /* cd0 */ + struct grub_edd_info eddbuf[EDDMAXNR]; /* d00 */ + grub_uint8_t _pad9[276]; /* eec */ } GRUB_PACKED; #endif /* ! ASM_FILE */ From 617dab9e476e8ea5aa314dcc5412bbd8a6f1f465 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Mon, 7 Apr 2025 16:29:15 +0800 Subject: [PATCH 377/402] tpm2_key_protector: Dump PCRs on policy fail PCR mismatch is one common cause of TPM key unsealing fail. Since the system may be compromised, it is not safe to boot into OS to get the PCR values and TPM eventlog for the further investigation. To provide some hints, GRUB now dumps PCRs on policy fail, so the user can check the current PCR values. PCR 0~15 are chosen to cover the firmware, bootloader, and OS. The sample output: PCR Mismatch! Check firmware and bootloader before typing passphrase! TPM PCR [sha256]: 00: 17401f37710984c1d8a03a81fff3ab567ae9291bac61e21715b890ee28879738 01: 7a114329ba388445a96e8db2a072785937c1b7a8803ed7cc682b87f3ff3dd7a8 02: 11c2776849e8e24b7d80c926cbc4257871bffa744dadfefd3ed049ce25143e05 03: 6c33b362073e28e30b47302bbdd3e6f9cee4debca3a304e646f8c68245724350 04: 62d38838483ecfd2484ee3a2e5450d8ca3b35fc72cda6a8c620f9f43521c37d1 05: d8a85cb37221ab7d1f2cc5f554dbe0463acb6784b5b8dc3164ccaa66d8fff0e1 06: 9262e37cbe71ed4daf815b4a4881fb7251c9d371092dde827557d5368121e10e 07: 219d542233be492d62b079ffe46cf13396a8c27e520e88b08eaf2e6d3b7e70f5 08: de1f61c973b673e505adebe0d7e8fb65fde6c24dd4ab4fbaff9e28b18df6ecd3 09: c1de7274fa3e879a16d7e6e7629e3463d95f68adcfd17c477183846dccc41c89 10: 0000000000000000000000000000000000000000000000000000000000000000 11: 0000000000000000000000000000000000000000000000000000000000000000 12: 0000000000000000000000000000000000000000000000000000000000000000 13: 0000000000000000000000000000000000000000000000000000000000000000 14: 9ab9ebe4879a7f4dd00c04f37e79cfd69d0dd7a8bcc6b01135525b67676a3e40 15: 0000000000000000000000000000000000000000000000000000000000000000 16: 0000000000000000000000000000000000000000000000000000000000000000 17: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 18: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 19: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 20: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 21: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 22: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 23: 0000000000000000000000000000000000000000000000000000000000000000 error: failed to unseal sealed key (TPM2_Unseal: 0x99d). error: no key protector provided a usable key for luks (af16e48f-746b-4a12-aae1-c14dcee429e0). If the user happens to have the PCR values for key sealing, the PCR dump can be used to identify the changed PCRs and narrow down the scope for closer inspection. Please note that the PCR dump is trustworthy only if the GRUB binary is authentic, so the user has to check the GRUB binary thoroughly before using the PCR dump. Signed-off-by: Gary Lin Reviewed-by: Stefan Berger Reviewed-by: Daniel Kiper --- .../commands/tpm2_key_protector/module.c | 118 +++++++++++++++++- 1 file changed, 114 insertions(+), 4 deletions(-) diff --git a/grub-core/commands/tpm2_key_protector/module.c b/grub-core/commands/tpm2_key_protector/module.c index 74e79a545..d5e530f77 100644 --- a/grub-core/commands/tpm2_key_protector/module.c +++ b/grub-core/commands/tpm2_key_protector/module.c @@ -790,7 +790,7 @@ tpm2_protector_simple_policy_seq (const tpm2_protector_context_t *ctx, static grub_err_t tpm2_protector_unseal (tpm2key_policy_t policy_seq, TPM_HANDLE_t sealed_handle, - grub_uint8_t **key, grub_size_t *key_size) + grub_uint8_t **key, grub_size_t *key_size, bool *dump_pcr) { TPMS_AUTH_COMMAND_t authCmd = {0}; TPM2B_SENSITIVE_DATA_t data; @@ -801,6 +801,8 @@ tpm2_protector_unseal (tpm2key_policy_t policy_seq, TPM_HANDLE_t sealed_handle, TPM_RC_t rc; grub_err_t err; + *dump_pcr = false; + /* Start Auth Session */ nonceCaller.size = TPM_SHA256_DIGEST_SIZE; symmetric.algorithm = TPM_ALG_NULL; @@ -820,6 +822,13 @@ tpm2_protector_unseal (tpm2key_policy_t policy_seq, TPM_HANDLE_t sealed_handle, rc = grub_tpm2_unseal (sealed_handle, &authCmd, &data, NULL); if (rc != TPM_RC_SUCCESS) { + /* + * Trigger PCR dump on policy fail + * TPM_RC_S (0x800) | TPM_RC_1 (0x100) | RC_FMT (0x80) | TPM_RC_POLICY_FAIL (0x1D) + */ + if (rc == 0x99D) + *dump_pcr = true; + err = grub_error (GRUB_ERR_BAD_DEVICE, "failed to unseal sealed key (TPM2_Unseal: 0x%x)", rc); goto error; } @@ -845,6 +854,91 @@ tpm2_protector_unseal (tpm2key_policy_t policy_seq, TPM_HANDLE_t sealed_handle, return err; } +#define TPM_PCR_STR_SIZE (sizeof (TPMU_HA_t) * 2 + 1) + +static grub_err_t +tpm2_protector_get_pcr_str (const TPM_ALG_ID_t algo, grub_uint32_t index, char *pcr_str, grub_uint16_t buf_size) +{ + TPML_PCR_SELECTION_t pcr_sel = { + .count = 1, + .pcrSelections = { + { + .hash = algo, + .sizeOfSelect = 3, + .pcrSelect = {0} + }, + } + }; + TPML_DIGEST_t digest = {0}; + grub_uint16_t i; + TPM_RC_t rc; + + if (buf_size < TPM_PCR_STR_SIZE) + { + grub_snprintf (pcr_str, buf_size, "insufficient buffer"); + return GRUB_ERR_OUT_OF_MEMORY; + } + + TPMS_PCR_SELECTION_SelectPCR (&pcr_sel.pcrSelections[0], index); + + rc = grub_tpm2_pcr_read (NULL, &pcr_sel, NULL, NULL, &digest, NULL); + if (rc != TPM_RC_SUCCESS) + { + grub_snprintf (pcr_str, buf_size, "TPM2_PCR_Read: 0x%x", rc); + return GRUB_ERR_BAD_DEVICE; + } + + /* Check the returned digest number and size */ + if (digest.count != 1 || digest.digests[0].size > sizeof (TPMU_HA_t)) + { + grub_snprintf (pcr_str, buf_size, "invalid digest"); + return GRUB_ERR_BAD_DEVICE; + } + + /* Print the digest to the buffer */ + for (i = 0; i < digest.digests[0].size; i++) + grub_snprintf (pcr_str + 2 * i, buf_size - 2 * i, "%02x", digest.digests[0].buffer[i]); + + return GRUB_ERR_NONE; +} + +static void +tpm2_protector_dump_pcr (const TPM_ALG_ID_t bank) +{ + const char *algo_name; + char pcr_str[TPM_PCR_STR_SIZE]; + grub_uint8_t i; + grub_err_t err; + + if (bank == TPM_ALG_SHA1) + algo_name = "sha1"; + else if (bank == TPM_ALG_SHA256) + algo_name = "sha256"; + else if (bank == TPM_ALG_SHA384) + algo_name = "sha384"; + else if (bank == TPM_ALG_SHA512) + algo_name = "sha512"; + else + algo_name = "other"; + + /* Try to fetch PCR 0 */ + err = tpm2_protector_get_pcr_str (bank, 0, pcr_str, sizeof (pcr_str)); + if (err != GRUB_ERR_NONE) + { + grub_printf ("Unsupported PCR bank [%s]: %s\n", algo_name, pcr_str); + return; + } + + grub_printf ("TPM PCR [%s]:\n", algo_name); + + grub_printf (" %02d: %s\n", 0, pcr_str); + for (i = 1; i < TPM_MAX_PCRS; i++) + { + tpm2_protector_get_pcr_str (bank, i, pcr_str, sizeof (pcr_str)); + grub_printf (" %02d: %s\n", i, pcr_str); + } +} + static grub_err_t tpm2_protector_srk_recover (const tpm2_protector_context_t *ctx, grub_uint8_t **key, grub_size_t *key_size) @@ -859,6 +953,7 @@ tpm2_protector_srk_recover (const tpm2_protector_context_t *ctx, tpm2key_policy_t policy_seq = NULL; tpm2key_authpolicy_t authpol = NULL; tpm2key_authpolicy_t authpol_seq = NULL; + bool dump_pcr = false; grub_err_t err; /* @@ -924,7 +1019,7 @@ tpm2_protector_srk_recover (const tpm2_protector_context_t *ctx, /* Iterate the authpolicy sequence to find one that unseals the key */ FOR_LIST_ELEMENTS (authpol, authpol_seq) { - err = tpm2_protector_unseal (authpol->policy_seq, sealed_handle, key, key_size); + err = tpm2_protector_unseal (authpol->policy_seq, sealed_handle, key, key_size, &dump_pcr); if (err == GRUB_ERR_NONE) break; @@ -952,13 +1047,20 @@ tpm2_protector_srk_recover (const tpm2_protector_context_t *ctx, goto exit2; } - err = tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size); + err = tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size, &dump_pcr); } /* Pop error messages on success */ if (err == GRUB_ERR_NONE) while (grub_error_pop ()); + /* Dump PCRs if necessary */ + if (dump_pcr == true) + { + grub_printf ("PCR Mismatch! Check firmware and bootloader before typing passphrase!\n"); + tpm2_protector_dump_pcr (ctx->bank); + } + exit2: grub_tpm2_flushcontext (sealed_handle); @@ -978,6 +1080,7 @@ tpm2_protector_nv_recover (const tpm2_protector_context_t *ctx, { TPM_HANDLE_t sealed_handle = ctx->nv; tpm2key_policy_t policy_seq = NULL; + bool dump_pcr = false; grub_err_t err; /* Create a basic policy sequence based on the given PCR selection */ @@ -985,7 +1088,14 @@ tpm2_protector_nv_recover (const tpm2_protector_context_t *ctx, if (err != GRUB_ERR_NONE) goto exit; - err = tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size); + err = tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size, &dump_pcr); + + /* Dump PCRs if necessary */ + if (dump_pcr == true) + { + grub_printf ("PCR Mismatch! Check firmware and bootloader before typing passphrase!\n"); + tpm2_protector_dump_pcr (ctx->bank); + } exit: grub_tpm2_flushcontext (sealed_handle); From 46c9f3a8dac5274c8d117ea131ca5c5842f9276f Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Mon, 7 Apr 2025 16:29:16 +0800 Subject: [PATCH 378/402] tpm2_key_protector: Add tpm2_dump_pcr command The user may need to inspect the TPM 2.0 PCR values with the GRUB shell, so the new tpm2_dump_pcr command is added to print all PCRs of the specified bank. Also update the document for the new command. Signed-off-by: Gary Lin Tested-by: Stefan Berger Reviewed-by: Daniel Kiper --- docs/grub.texi | 13 +++++++ .../commands/tpm2_key_protector/module.c | 35 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/docs/grub.texi b/docs/grub.texi index d9b26fa36..54d3ab52f 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -6488,6 +6488,7 @@ you forget a command, you can run the command @command{help} * 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 +* tpm2_dump_pcr:: Dump TPM2 PCRs * true:: Do nothing, successfully * trust:: Add public key to list of trusted keys * unset:: Unset an environment variable @@ -8104,6 +8105,18 @@ key and unseal it with the given PCR list and bank. Clear the TPM2 key protector if previously initialized. @end deffn +@node tpm2_dump_pcr +@subsection tpm2_dump_pcr + +@deffn Command tpm2_dump_pcr [@var{bank}] +Print all PCRs of the specified TPM 2.0 @var{bank}. The supported banks are +@samp{sha1}, @samp{sha256}, @samp{sha384}, and @samp{sha512}. If @var{bank} +is not specified, @samp{sha256} is chosen by default. + +Since GRUB measures every command into PCR 8, invoking @command{tpm2_dump_pcr} +also extends PCR 8, so PCR 8 will not be a stable value in GRUB shell. +@end deffn + @node true @subsection true diff --git a/grub-core/commands/tpm2_key_protector/module.c b/grub-core/commands/tpm2_key_protector/module.c index d5e530f77..0a5d81e4c 100644 --- a/grub-core/commands/tpm2_key_protector/module.c +++ b/grub-core/commands/tpm2_key_protector/module.c @@ -160,6 +160,8 @@ static grub_extcmd_t tpm2_protector_init_cmd; static grub_extcmd_t tpm2_protector_clear_cmd; static tpm2_protector_context_t tpm2_protector_ctx = {0}; +static grub_command_t tpm2_dump_pcr_cmd; + static grub_err_t tpm2_protector_srk_read_file (const char *filepath, void **buffer, grub_size_t *buffer_size) { @@ -1315,6 +1317,33 @@ static struct grub_key_protector tpm2_key_protector = .recover_key = tpm2_protector_recover_key }; +static grub_err_t +tpm2_dump_pcr (grub_command_t cmd __attribute__((__unused__)), + int argc, char *argv[]) +{ + TPM_ALG_ID_t pcr_bank; + + if (argc == 0) + pcr_bank = TPM_ALG_SHA256; + else if (grub_strcmp (argv[0], "sha1") == 0) + pcr_bank = TPM_ALG_SHA1; + else if (grub_strcmp (argv[0], "sha256") == 0) + pcr_bank = TPM_ALG_SHA256; + else if (grub_strcmp (argv[0], "sha384") == 0) + pcr_bank = TPM_ALG_SHA384; + else if (grub_strcmp (argv[0], "sha512") == 0) + pcr_bank = TPM_ALG_SHA512; + else + { + grub_printf ("Unknown PCR bank\n"); + return GRUB_ERR_BAD_ARGUMENT; + } + + tpm2_protector_dump_pcr (pcr_bank); + + return GRUB_ERR_NONE; +} + GRUB_MOD_INIT (tpm2_key_protector) { tpm2_protector_init_cmd = @@ -1336,6 +1365,10 @@ GRUB_MOD_INIT (tpm2_key_protector) N_("Clear the TPM2 key protector if previously initialized."), NULL); grub_key_protector_register (&tpm2_key_protector); + + tpm2_dump_pcr_cmd = + grub_register_command ("tpm2_dump_pcr", tpm2_dump_pcr, N_("Dump TPM2 PCRs"), + N_("Print all PCRs of the specified TPM 2.0 bank")); } GRUB_MOD_FINI (tpm2_key_protector) @@ -1345,4 +1378,6 @@ GRUB_MOD_FINI (tpm2_key_protector) grub_key_protector_unregister (&tpm2_key_protector); grub_unregister_extcmd (tpm2_protector_clear_cmd); grub_unregister_extcmd (tpm2_protector_init_cmd); + + grub_unregister_command (tpm2_dump_pcr_cmd); } From 041164d00e79ffd2433675a5dd5b824833b9fc6a Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Mon, 7 Apr 2025 16:29:17 +0800 Subject: [PATCH 379/402] tss2: Fix the missing authCommand grub_tpm2_readpublic() and grub_tpm2_testparms() didn't check authCommand when marshaling the input data buffer. Currently, there is no caller using non-NULL authCommand. However, to avoid the potential issue, the conditional check is added to insert authCommand into the input buffer if necessary. Also fix a few pointer checks. Signed-off-by: Gary Lin Reviewed-by: Stefan Berger Reviewed-by: Daniel Kiper --- grub-core/lib/tss2/tpm2_cmd.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/grub-core/lib/tss2/tpm2_cmd.c b/grub-core/lib/tss2/tpm2_cmd.c index cd0c6fd31..211d807d5 100644 --- a/grub-core/lib/tss2/tpm2_cmd.c +++ b/grub-core/lib/tss2/tpm2_cmd.c @@ -341,6 +341,8 @@ grub_tpm2_readpublic (const TPMI_DH_OBJECT_t objectHandle, /* Marshal */ grub_tpm2_buffer_init (&in); grub_tpm2_buffer_pack_u32 (&in, objectHandle); + if (authCommand != NULL) + grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); if (in.error != 0) return TPM_RC_FAILURE; @@ -398,7 +400,7 @@ grub_tpm2_load (const TPMI_DH_OBJECT_t parent_handle, /* Marshal */ grub_tpm2_buffer_init (&in); grub_tpm2_buffer_pack_u32 (&in, parent_handle); - if (authCommand) + if (authCommand != NULL) grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); grub_Tss2_MU_TPM2B_Marshal (&in, inPrivate->size, inPrivate->buffer); grub_Tss2_MU_TPM2B_PUBLIC_Marshal (&in, inPublic); @@ -461,9 +463,9 @@ grub_tpm2_loadexternal (const TPMS_AUTH_COMMAND_t *authCommand, /* Marshal */ grub_tpm2_buffer_init (&in); - if (authCommand) + if (authCommand != NULL) grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); - if (inPrivate) + if (inPrivate != NULL) grub_Tss2_MU_TPM2B_SENSITIVE_Marshal (&in, inPrivate); else grub_tpm2_buffer_pack_u16 (&in, 0); @@ -1023,6 +1025,8 @@ grub_tpm2_testparms (const TPMT_PUBLIC_PARMS_t *parms, /* Marshal */ grub_tpm2_buffer_init (&in); grub_Tss2_MU_TPMT_PUBLIC_PARMS_Marshal (&in, parms); + if (authCommand != NULL) + grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); if (in.error != 0) return TPM_RC_FAILURE; From 75c480885ab00fb9bc046fe214df60007116aef2 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Mon, 7 Apr 2025 16:29:18 +0800 Subject: [PATCH 380/402] tss2: Add TPM 2.0 NV index commands The following TPM 2.0 commands are introduced to tss2 to access the TPM non-volatile memory associated with the NV index handles: - TPM2_NV_DefineSpace, - TPM2_NV_UndefineSpace, - TPM2_NV_ReadPublic, - TPM2_NV_Read, - TPM2_NV_Write. The related marshal/unmarshal functions are also introduced. Signed-off-by: Gary Lin Reviewed-by: Stefan Berger Reviewed-by: Daniel Kiper --- grub-core/lib/tss2/tpm2_cmd.c | 201 ++++++++++++++++++++++++++++++++ grub-core/lib/tss2/tpm2_cmd.h | 32 +++++ grub-core/lib/tss2/tss2_mu.c | 39 +++++++ grub-core/lib/tss2/tss2_mu.h | 12 ++ grub-core/lib/tss2/tss2_types.h | 6 + 5 files changed, 290 insertions(+) diff --git a/grub-core/lib/tss2/tpm2_cmd.c b/grub-core/lib/tss2/tpm2_cmd.c index 211d807d5..6d25db1ab 100644 --- a/grub-core/lib/tss2/tpm2_cmd.c +++ b/grub-core/lib/tss2/tpm2_cmd.c @@ -1045,3 +1045,204 @@ grub_tpm2_testparms (const TPMT_PUBLIC_PARMS_t *parms, return TPM_RC_SUCCESS; } + +TPM_RC_t +grub_tpm2_nv_definespace (const TPMI_RH_PROVISION_t authHandle, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_AUTH_t *auth, + const TPM2B_NV_PUBLIC_t *publicInfo) +{ + TPM_RC_t rc; + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + TPMI_ST_COMMAND_TAG_t tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; + TPM_RC_t responseCode; + + if (publicInfo == NULL) + return TPM_RC_VALUE; + + /* Marshal */ + grub_tpm2_buffer_init (&in); + grub_tpm2_buffer_pack_u32 (&in, authHandle); + if (authCommand != NULL) + grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); + if (auth != NULL) + grub_Tss2_MU_TPM2B_Marshal (&in, auth->size, auth->buffer); + else + grub_tpm2_buffer_pack_u16 (&in, 0); + grub_Tss2_MU_TPM2B_NV_PUBLIC_Marshal (&in, publicInfo); + if (in.error != 0) + 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; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal */ + if (out.error != 0) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} + +TPM_RC_t +grub_tpm2_nv_undefinespace (const TPMI_RH_PROVISION_t authHandle, + const TPMI_RH_NV_INDEX_t nvIndex, + const TPMS_AUTH_COMMAND_t *authCommand) +{ + TPM_RC_t rc; + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + TPMI_ST_COMMAND_TAG_t tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; + TPM_RC_t responseCode; + + /* Marshal */ + grub_tpm2_buffer_init (&in); + grub_tpm2_buffer_pack_u32 (&in, authHandle); + grub_tpm2_buffer_pack_u32 (&in, nvIndex); + if (authCommand != NULL) + grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); + if (in.error != 0) + 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; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal */ + if (out.error != 0) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} + +TPM_RC_t +grub_tpm2_nv_readpublic (const TPMI_RH_NV_INDEX_t nvIndex, + const TPMS_AUTH_COMMAND_t *authCommand, + TPM2B_NV_PUBLIC_t *nvPublic, + TPM2B_NAME_t *nvName) +{ + TPM_RC_t rc; + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + TPMI_ST_COMMAND_TAG_t tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; + TPM_RC_t responseCode; + grub_uint32_t param_size; + + /* Marshal */ + grub_tpm2_buffer_init (&in); + grub_tpm2_buffer_pack_u32 (&in, nvIndex); + if (authCommand != NULL) + grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); + if (in.error != 0) + 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; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal */ + if (tag == TPM_ST_SESSIONS) + grub_tpm2_buffer_unpack_u32 (&out, ¶m_size); + grub_Tss2_MU_TPM2B_NV_PUBLIC_Unmarshal (&out, nvPublic); + grub_Tss2_MU_TPM2B_NAME_Unmarshal (&out, nvName); + if (out.error != 0) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} + +TPM_RC_t +grub_tpm2_nv_read (const TPMI_RH_NV_AUTH_t authHandle, + const TPMI_RH_NV_INDEX_t nvIndex, + const TPMS_AUTH_COMMAND_t *authCommand, + const grub_uint16_t size, + const grub_uint16_t offset, + TPM2B_MAX_NV_BUFFER_t *data) +{ + TPM_RC_t rc; + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + TPMI_ST_COMMAND_TAG_t tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; + TPM_RC_t responseCode; + grub_uint32_t param_size; + + /* Marshal */ + grub_tpm2_buffer_init (&in); + grub_tpm2_buffer_pack_u32 (&in, authHandle); + grub_tpm2_buffer_pack_u32 (&in, nvIndex); + if (authCommand != NULL) + grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); + grub_tpm2_buffer_pack_u16 (&in, size); + grub_tpm2_buffer_pack_u16 (&in, offset); + if (in.error != 0) + 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; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal */ + if (tag == TPM_ST_SESSIONS) + grub_tpm2_buffer_unpack_u32 (&out, ¶m_size); + grub_Tss2_MU_TPM2B_NAX_NV_BUFFER_Unmarshal (&out, data); + if (out.error != 0) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} + +TPM_RC_t +grub_tpm2_nv_write (const TPMI_RH_NV_AUTH_t authHandle, + const TPMI_RH_NV_INDEX_t nvIndex, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_MAX_NV_BUFFER_t *data, + const grub_uint16_t offset) +{ + TPM_RC_t rc; + struct grub_tpm2_buffer in; + struct grub_tpm2_buffer out; + TPMI_ST_COMMAND_TAG_t tag = authCommand ? TPM_ST_SESSIONS : TPM_ST_NO_SESSIONS; + TPM_RC_t responseCode; + + /* Marshal */ + grub_tpm2_buffer_init (&in); + grub_tpm2_buffer_pack_u32 (&in, authHandle); + grub_tpm2_buffer_pack_u32 (&in, nvIndex); + if (authCommand != NULL) + grub_Tss2_MU_TPMS_AUTH_COMMAND_Marshal (&in, authCommand); + grub_Tss2_MU_TPM2B_Marshal (&in, data->size, data->buffer); + grub_tpm2_buffer_pack_u16 (&in, offset); + if (in.error != 0) + 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; + if (responseCode != TPM_RC_SUCCESS) + return responseCode; + + /* Unmarshal */ + if (out.error != 0) + return TPM_RC_FAILURE; + + return TPM_RC_SUCCESS; +} diff --git a/grub-core/lib/tss2/tpm2_cmd.h b/grub-core/lib/tss2/tpm2_cmd.h index d313cba00..90b42efec 100644 --- a/grub-core/lib/tss2/tpm2_cmd.h +++ b/grub-core/lib/tss2/tpm2_cmd.h @@ -154,4 +154,36 @@ extern TPM_RC_t grub_tpm2_testparms (const TPMT_PUBLIC_PARMS_t *parms, const TPMS_AUTH_COMMAND_t *authCommand); +extern TPM_RC_t +grub_tpm2_nv_definespace (const TPMI_RH_PROVISION_t authHandle, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_AUTH_t *auth, + const TPM2B_NV_PUBLIC_t *publicInfo); + +extern TPM_RC_t +grub_tpm2_nv_undefinespace (const TPMI_RH_PROVISION_t authHandle, + const TPMI_RH_NV_INDEX_t nvIndex, + const TPMS_AUTH_COMMAND_t *authCommand); + +extern TPM_RC_t +grub_tpm2_nv_readpublic (const TPMI_RH_NV_INDEX_t nvIndex, + const TPMS_AUTH_COMMAND_t *authCommand, + TPM2B_NV_PUBLIC_t *nvPublic, + TPM2B_NAME_t *nvName); + +extern TPM_RC_t +grub_tpm2_nv_read (const TPMI_RH_NV_AUTH_t authHandle, + const TPMI_RH_NV_INDEX_t nvIndex, + const TPMS_AUTH_COMMAND_t *authCommand, + const grub_uint16_t size, + const grub_uint16_t offset, + TPM2B_MAX_NV_BUFFER_t *data); + +extern TPM_RC_t +grub_tpm2_nv_write (const TPMI_RH_NV_AUTH_t authHandle, + const TPMI_RH_NV_INDEX_t nvIndex, + const TPMS_AUTH_COMMAND_t *authCommand, + const TPM2B_MAX_NV_BUFFER_t *data, + const grub_uint16_t offset); + #endif /* ! GRUB_TPM2_COMMANDS_HEADER */ diff --git a/grub-core/lib/tss2/tss2_mu.c b/grub-core/lib/tss2/tss2_mu.c index 86134cc0a..816e5b37f 100644 --- a/grub-core/lib/tss2/tss2_mu.c +++ b/grub-core/lib/tss2/tss2_mu.c @@ -17,6 +17,7 @@ * along with GRUB. If not, see . */ +#include #include #include @@ -572,6 +573,37 @@ grub_Tss2_MU_TPMT_TK_VERIFIED_Marshal (grub_tpm2_buffer_t buffer, grub_Tss2_MU_TPM2B_Marshal (buffer, p->digest.size, p->digest.buffer); } +void +grub_Tss2_MU_TPMS_NV_PUBLIC_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_NV_PUBLIC_t *p) +{ + grub_tpm2_buffer_pack_u32 (buffer, p->nvIndex); + grub_tpm2_buffer_pack_u16 (buffer, p->nameAlg); + grub_tpm2_buffer_pack_u32 (buffer, p->attributes); + grub_Tss2_MU_TPM2B_Marshal (buffer, p->authPolicy.size, p->authPolicy.buffer); + grub_tpm2_buffer_pack_u16 (buffer, p->dataSize); +} + +void +grub_Tss2_MU_TPM2B_NV_PUBLIC_Marshal (grub_tpm2_buffer_t buffer, + const TPM2B_NV_PUBLIC_t *p) +{ + grub_uint32_t start; + grub_uint16_t size; + + if (p != NULL) + { + grub_tpm2_buffer_pack_u16 (buffer, p->size); + + start = buffer->size; + grub_Tss2_MU_TPMS_NV_PUBLIC_Marshal (buffer, &p->nvPublic); + size = grub_cpu_to_be16 (buffer->size - start); + grub_memcpy (&buffer->data[start - sizeof (grub_uint16_t)], &size, sizeof (size)); + } + else + grub_tpm2_buffer_pack_u16 (buffer, 0); +} + static void __Tss2_MU_TPM2B_BUFFER_Unmarshal (grub_tpm2_buffer_t buffer, TPM2B_t *p, grub_uint16_t bound) @@ -982,6 +1014,13 @@ grub_Tss2_MU_TPM2B_NV_PUBLIC_Unmarshal (grub_tpm2_buffer_t buffer, grub_Tss2_MU_TPMS_NV_PUBLIC_Unmarshal (buffer, &p->nvPublic); } +void +grub_Tss2_MU_TPM2B_NAX_NV_BUFFER_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_MAX_NV_BUFFER_t *p) +{ + TPM2B_BUFFER_UNMARSHAL (buffer, TPM2B_MAX_NV_BUFFER_t, p); +} + void grub_Tss2_MU_TPM2B_NAME_Unmarshal (grub_tpm2_buffer_t buffer, TPM2B_NAME_t *n) diff --git a/grub-core/lib/tss2/tss2_mu.h b/grub-core/lib/tss2/tss2_mu.h index 8f82126e1..6440de57c 100644 --- a/grub-core/lib/tss2/tss2_mu.h +++ b/grub-core/lib/tss2/tss2_mu.h @@ -193,6 +193,14 @@ extern void grub_Tss2_MU_TPMT_TK_VERIFIED_Marshal (grub_tpm2_buffer_t buffer, const TPMT_TK_VERIFIED_t *p); +extern void +grub_Tss2_MU_TPMS_NV_PUBLIC_Marshal (grub_tpm2_buffer_t buffer, + const TPMS_NV_PUBLIC_t *p); + +extern void +grub_Tss2_MU_TPM2B_NV_PUBLIC_Marshal (grub_tpm2_buffer_t buffer, + const TPM2B_NV_PUBLIC_t *p); + extern void grub_Tss2_MU_TPMS_AUTH_RESPONSE_Unmarshal (grub_tpm2_buffer_t buffer, TPMS_AUTH_RESPONSE_t *p); @@ -336,6 +344,10 @@ extern void grub_Tss2_MU_TPM2B_NV_PUBLIC_Unmarshal (grub_tpm2_buffer_t buffer, TPM2B_NV_PUBLIC_t *p); +extern void +grub_Tss2_MU_TPM2B_NAX_NV_BUFFER_Unmarshal (grub_tpm2_buffer_t buffer, + TPM2B_MAX_NV_BUFFER_t *p); + extern void grub_Tss2_MU_TPM2B_NAME_Unmarshal (grub_tpm2_buffer_t buffer, TPM2B_NAME_t *n); diff --git a/grub-core/lib/tss2/tss2_types.h b/grub-core/lib/tss2/tss2_types.h index 5b1a7947d..bddde7191 100644 --- a/grub-core/lib/tss2/tss2_types.h +++ b/grub-core/lib/tss2/tss2_types.h @@ -270,6 +270,7 @@ typedef TPM_HANDLE_t TPMI_RH_NV_INDEX_t; /* TPM_HT_t Constants */ typedef grub_uint8_t TPM_HT_t; +#define TPM_HT_NV_INDEX ((TPM_HT_t) 0x01) #define TPM_HT_PERMANENT ((TPM_HT_t) 0x40) #define TPM_HT_PERSISTENT ((TPM_HT_t) 0x81) @@ -300,6 +301,7 @@ typedef TPM_HANDLE_t TPM_HC_t; #define TPM_HR_HANDLE_MASK ((TPM_HC_t) 0x00FFFFFF) #define TPM_HR_RANGE_MASK ((TPM_HC_t) 0xFF000000) #define TPM_HR_SHIFT ((TPM_HC_t) 24) +#define TPM_HR_NV_INDEX ((TPM_HC_t) (TPM_HT_NV_INDEX << TPM_HR_SHIFT)) #define TPM_HR_PERSISTENT ((TPM_HC_t) (TPM_HT_PERSISTENT << TPM_HR_SHIFT)) #define TPM_HR_PERMANENT ((TPM_HC_t) (TPM_HT_PERMANENT << TPM_HR_SHIFT)) #define TPM_PERSISTENT_FIRST ((TPM_HC_t) (TPM_HR_PERSISTENT + 0)) @@ -308,6 +310,7 @@ typedef TPM_HANDLE_t TPM_HC_t; #define TPM_PERMANENT_LAST ((TPM_HC_t) TPM_RH_LAST) /* TPM Handle Type Checks */ +#define TPM_HT_IS_NVINDEX(HANDLE) (((HANDLE) >> TPM_HR_SHIFT) == TPM_HT_NV_INDEX) #define TPM_HT_IS_PERMANENT(HANDLE) (((HANDLE) >> TPM_HR_SHIFT) == TPM_HT_PERMANENT) #define TPM_HT_IS_PERSISTENT(HANDLE) (((HANDLE) >> TPM_HR_SHIFT) == TPM_HT_PERSISTENT) @@ -334,8 +337,11 @@ typedef grub_uint32_t TPM_CC_t; #define TPM_CC_ReadPublic ((TPM_CC_t) 0x00000173) #define TPM_CC_StartAuthSession ((TPM_CC_t) 0x00000176) #define TPM_CC_PolicyPCR ((TPM_CC_t) 0x0000017f) +#define TPM_CC_NV_DefineSpace ((TPM_CC_t) 0x0000012a) #define TPM_CC_NV_Read ((TPM_CC_t) 0x0000014e) #define TPM_CC_NV_ReadPublic ((TPM_CC_t) 0x00000169) +#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_Read ((TPM_CC_t) 0x0000017e) #define TPM_CC_Load ((TPM_CC_t) 0x00000157) From fa69deac565e5d5015ca396b017239cd96900673 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Mon, 7 Apr 2025 16:29:19 +0800 Subject: [PATCH 381/402] tpm2_key_protector: Unseal key from a buffer Extract the logic to handle the file buffer from the SRK recover function to prepare to load the sealed key from the NV index handle, so the NV index mode can share the same code path in the later patch. The SRK recover function now only reads the file and sends the file buffer to the new function. Besides this, to avoid introducing more options for the NV index mode, the file format is detected automatically before unmarshaling the data, so there is no need to use the command option to specify the file format anymore. In other words, "-T" and "-k" are the same now. Also update grub.text to address the change. Signed-off-by: Gary Lin Reviewed-by: Stefan Berger Reviewed-by: Daniel Kiper --- docs/grub.texi | 13 +- .../commands/tpm2_key_protector/module.c | 122 +++++++++++++----- 2 files changed, 96 insertions(+), 39 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index 54d3ab52f..ae89838bc 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -8075,12 +8075,13 @@ options are @option{-T}, @option{-k}, @option{-a}, and @option{-s}. On the other hand, the NV index-specific option is @option{-n}. The key file for SRK mode can be supplied with either @option{-T} or -@option{-k}. The @option{-T} option is for the path to the key file in -TPM 2.0 Key File format. Since the parameters for the TPM commands are written -in the file, there is no need to set the PCR list(@option{-p}) and -bank(@option{-b}) when using the @option{-T} option. The @option{-k} option -is for the key file in the raw format, and the @option{-p} and @option{-b} -options are necessary for the non-default PCR list or bank. In general, +@option{-k}. Those two options were used to distinguish the file formats but +are same now. There are two supported file formats: raw format and TPM 2.0 +Key File format. When using the key file in the raw format, the @option{-p} +and @option{-b} options are necessary for the non-default PCR list or bank. +On the other hand, when using the key file in TPM 2.0 Key File format, the +the parameters for the TPM commands are written in the file, and there is no +need to set the PCR list(@option{-p}) and bank(@option{-b}). In general, TPM 2.0 Key File format is preferred due to the simplified GRUB command options and the authorized policy support diff --git a/grub-core/commands/tpm2_key_protector/module.c b/grub-core/commands/tpm2_key_protector/module.c index 0a5d81e4c..795f61518 100644 --- a/grub-core/commands/tpm2_key_protector/module.c +++ b/grub-core/commands/tpm2_key_protector/module.c @@ -218,10 +218,51 @@ tpm2_protector_srk_read_file (const char *filepath, void **buffer, grub_size_t * return err; } +/* Check if the data is in TPM 2.0 Key File format */ +static bool +tpm2_protector_is_tpm2key (grub_uint8_t *buffer, grub_size_t buffer_size) +{ + /* id-sealedkey OID (2.23.133.10.1.5) in DER */ + const grub_uint8_t sealed_key_oid[] = {0x06, 0x06, 0x67, 0x81, 0x05, 0x0a}; + grub_size_t skip = 0; + + /* Need at least the first two bytes to check the tag and the length */ + if (buffer_size < 2) + return false; + + /* The first byte is always 0x30 (SEQUENCE). */ + if (buffer[0] != 0x30) + return false; + + /* + * Get the bytes of the length + * + * If the bit 8 of the second byte is 0, it is in the short form, so the second byte + * alone represents the length. Thus, the first two bytes are skipped. + * + * Otherwise, it is in the long form, and bits 1~7 indicate how many more bytes are in + * the length field, so we skip the first two bytes plus the bytes for the length. + */ + if ((buffer[1] & 0x80) == 0) + skip = 2; + else + skip = (buffer[1] & 0x7F) + 2; + + /* Make sure the buffer is large enough to contain id-sealedkey OID */ + if (buffer_size < skip + sizeof (sealed_key_oid)) + return false; + + /* Check id-sealedkey OID */ + if (grub_memcmp (buffer + skip, sealed_key_oid, sizeof (sealed_key_oid)) != 0) + return false; + + return true; +} + static grub_err_t -tpm2_protector_srk_unmarshal_keyfile (void *sealed_key, - grub_size_t sealed_key_size, - tpm2_sealed_key_t *sk) +tpm2_protector_unmarshal_raw (void *sealed_key, + grub_size_t sealed_key_size, + tpm2_sealed_key_t *sk) { struct grub_tpm2_buffer buf; @@ -242,13 +283,13 @@ tpm2_protector_srk_unmarshal_keyfile (void *sealed_key, } static grub_err_t -tpm2_protector_srk_unmarshal_tpm2key (void *sealed_key, - grub_size_t sealed_key_size, - tpm2key_policy_t *policy_seq, - tpm2key_authpolicy_t *authpol_seq, - grub_uint8_t *rsaparent, - grub_uint32_t *parent, - tpm2_sealed_key_t *sk) +tpm2_protector_unmarshal_tpm2key (void *sealed_key, + grub_size_t sealed_key_size, + tpm2key_policy_t *policy_seq, + tpm2key_authpolicy_t *authpol_seq, + grub_uint8_t *rsaparent, + grub_uint32_t *parent, + tpm2_sealed_key_t *sk) { asn1_node tpm2key = NULL; grub_uint8_t rsaparent_tmp; @@ -942,12 +983,11 @@ tpm2_protector_dump_pcr (const TPM_ALG_ID_t bank) } static grub_err_t -tpm2_protector_srk_recover (const tpm2_protector_context_t *ctx, - grub_uint8_t **key, grub_size_t *key_size) +tpm2_protector_key_from_buffer (const tpm2_protector_context_t *ctx, + void *buffer, grub_size_t buf_size, + grub_uint8_t **key, grub_size_t *key_size) { tpm2_sealed_key_t sealed_key = {0}; - void *file_bytes = NULL; - grub_size_t file_size = 0; grub_uint8_t rsaparent = 0; TPM_HANDLE_t parent_handle = 0; TPM_HANDLE_t srk_handle = 0; @@ -960,22 +1000,17 @@ tpm2_protector_srk_recover (const tpm2_protector_context_t *ctx, /* * Retrieve sealed key, parent handle, policy sequence, and authpolicy - * sequence from the key file + * sequence from the buffer */ - if (ctx->tpm2key != NULL) + if (tpm2_protector_is_tpm2key (buffer, buf_size) == true) { - err = tpm2_protector_srk_read_file (ctx->tpm2key, &file_bytes, - &file_size); - if (err != GRUB_ERR_NONE) - return err; - - err = tpm2_protector_srk_unmarshal_tpm2key (file_bytes, - file_size, - &policy_seq, - &authpol_seq, - &rsaparent, - &parent_handle, - &sealed_key); + err = tpm2_protector_unmarshal_tpm2key (buffer, + buf_size, + &policy_seq, + &authpol_seq, + &rsaparent, + &parent_handle, + &sealed_key); if (err != GRUB_ERR_NONE) goto exit1; @@ -991,12 +1026,8 @@ tpm2_protector_srk_recover (const tpm2_protector_context_t *ctx, } else { - err = tpm2_protector_srk_read_file (ctx->keyfile, &file_bytes, &file_size); - if (err != GRUB_ERR_NONE) - return err; - parent_handle = TPM_RH_OWNER; - err = tpm2_protector_srk_unmarshal_keyfile (file_bytes, file_size, &sealed_key); + err = tpm2_protector_unmarshal_raw (buffer, buf_size, &sealed_key); if (err != GRUB_ERR_NONE) goto exit1; } @@ -1072,6 +1103,31 @@ tpm2_protector_srk_recover (const tpm2_protector_context_t *ctx, exit1: grub_tpm2key_free_policy_seq (policy_seq); grub_tpm2key_free_authpolicy_seq (authpol_seq); + return err; +} + +static grub_err_t +tpm2_protector_srk_recover (const tpm2_protector_context_t *ctx, + grub_uint8_t **key, grub_size_t *key_size) +{ + const char *filepath; + void *file_bytes = NULL; + grub_size_t file_size = 0; + grub_err_t err; + + if (ctx->tpm2key != NULL) + filepath = ctx->tpm2key; + else if (ctx->keyfile != NULL) + filepath = ctx->keyfile; + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("key file not specified")); + + err = tpm2_protector_srk_read_file (filepath, &file_bytes, &file_size); + if (err != GRUB_ERR_NONE) + return err; + + err = tpm2_protector_key_from_buffer (ctx, file_bytes, file_size, key, key_size); + grub_free (file_bytes); return err; } From cd9cb944d973da6a031d06183cc6da974beb4bae Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Mon, 7 Apr 2025 16:29:20 +0800 Subject: [PATCH 382/402] tpm2_key_protector: Support NV index handles Previously, NV index mode only supported persistent handles which are only for TPM objects. On the other hand, the "NV index" handle allows the user-defined data, so it can be an alternative to the key file and support TPM 2.0 Key File format immediately. The following tpm2-tools commands store the given key file, sealed.tpm, in either TPM 2.0 Key File format or the raw format into the NV index handle 0x1000000. # tpm2_nvdefine -C o \ -a "ownerread|ownerwrite" \ -s $(stat -c %s sealed.tpm) \ 0x1000000 # tpm2_nvwrite -C o -i sealed.tpm 0x1000000 To unseal the key in GRUB, add the "tpm2_key_protector_init" command to grub.cfg: tpm2_key_protector_init --mode=nv --nvindex=0x1000000 cryptomount -u --protector tpm2 To remove the NV index handle: # tpm2_nvundefine -C o 0x1000000 Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- .../commands/tpm2_key_protector/module.c | 70 ++++++++++++++++--- 1 file changed, 60 insertions(+), 10 deletions(-) diff --git a/grub-core/commands/tpm2_key_protector/module.c b/grub-core/commands/tpm2_key_protector/module.c index 795f61518..857f3753f 100644 --- a/grub-core/commands/tpm2_key_protector/module.c +++ b/grub-core/commands/tpm2_key_protector/module.c @@ -1133,10 +1133,9 @@ tpm2_protector_srk_recover (const tpm2_protector_context_t *ctx, } static grub_err_t -tpm2_protector_nv_recover (const tpm2_protector_context_t *ctx, - grub_uint8_t **key, grub_size_t *key_size) +tpm2_protector_load_persistent (const tpm2_protector_context_t *ctx, TPM_HANDLE_t sealed_handle, + grub_uint8_t **key, grub_size_t *key_size) { - TPM_HANDLE_t sealed_handle = ctx->nv; tpm2key_policy_t policy_seq = NULL; bool dump_pcr = false; grub_err_t err; @@ -1163,6 +1162,51 @@ tpm2_protector_nv_recover (const tpm2_protector_context_t *ctx, return err; } +static grub_err_t +tpm2_protector_key_from_nvindex (const tpm2_protector_context_t *ctx, TPM_HANDLE_t nvindex, + grub_uint8_t **key, grub_size_t *key_size) +{ + TPMS_AUTH_COMMAND_t authCmd = {0}; + TPM2B_NV_PUBLIC_t nv_public; + TPM2B_NAME_t nv_name; + grub_uint16_t data_size; + TPM2B_MAX_NV_BUFFER_t data; + TPM_RC_t rc; + + /* Get the data size in the NV index handle */ + rc = grub_tpm2_nv_readpublic (nvindex, NULL, &nv_public, &nv_name); + if (rc != TPM_RC_SUCCESS) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to retrieve info from 0x%x (TPM2_NV_ReadPublic: 0x%x)", nvindex, rc); + + data_size = nv_public.nvPublic.dataSize; + if (data_size > TPM_MAX_NV_BUFFER_SIZE) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "insufficient data buffer"); + + /* Read the data from the NV index handle */ + authCmd.sessionHandle = TPM_RS_PW; + rc = grub_tpm2_nv_read (TPM_RH_OWNER, nvindex, &authCmd, data_size, 0, &data); + if (rc != TPM_RC_SUCCESS) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to read data from 0x%x (TPM2_NV_Read: 0x%x)", nvindex, rc); + + return tpm2_protector_key_from_buffer (ctx, data.buffer, data_size, key, key_size); +} + +static grub_err_t +tpm2_protector_nv_recover (const tpm2_protector_context_t *ctx, + grub_uint8_t **key, grub_size_t *key_size) +{ + grub_err_t err; + + if (TPM_HT_IS_PERSISTENT (ctx->nv) == true) + err = tpm2_protector_load_persistent (ctx, ctx->nv, key, key_size); + else if (TPM_HT_IS_NVINDEX (ctx->nv) == true) + err = tpm2_protector_key_from_nvindex (ctx, ctx->nv, key, key_size); + else + err = GRUB_ERR_BAD_ARGUMENT; + + return err; +} + static grub_err_t tpm2_protector_recover (const tpm2_protector_context_t *ctx, grub_uint8_t **key, grub_size_t *key_size) @@ -1215,14 +1259,15 @@ tpm2_protector_check_args (tpm2_protector_context_t *ctx) if (ctx->mode == TPM2_PROTECTOR_MODE_NV && (ctx->tpm2key != NULL || ctx->keyfile != NULL)) - return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("in NV Index mode, a keyfile cannot be specified")); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("a key file cannot be specified when using NV index mode")); - if (ctx->mode == TPM2_PROTECTOR_MODE_NV && ctx->srk != 0) - return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("in NV Index mode, an SRK cannot be specified")); + if (ctx->mode == TPM2_PROTECTOR_MODE_NV && TPM_HT_IS_PERSISTENT (ctx->nv) == true && + (ctx->srk != 0 || ctx->srk_type.type != TPM_ALG_ERROR)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("an SRK cannot be specified when using NV index mode with a persistent handle")); if (ctx->mode == TPM2_PROTECTOR_MODE_NV && - ctx->srk_type.type != TPM_ALG_ERROR) - return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("in NV Index mode, an asymmetric key type cannot be specified")); + (TPM_HT_IS_PERSISTENT (ctx->nv) == false && TPM_HT_IS_NVINDEX (ctx->nv) == false)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("an NV index must be either a persistent handle or an NV index handle when using NV index mode")); /* Defaults assignment */ if (ctx->bank == TPM_ALG_ERROR) @@ -1234,8 +1279,13 @@ tpm2_protector_check_args (tpm2_protector_context_t *ctx) ctx->pcr_count = 1; } - if (ctx->mode == TPM2_PROTECTOR_MODE_SRK && - ctx->srk_type.type == TPM_ALG_ERROR) + /* + * Set ECC_NIST_P256 as the default SRK when using SRK mode or NV mode with + * an NV index handle + */ + if (ctx->srk_type.type == TPM_ALG_ERROR && + (ctx->mode == TPM2_PROTECTOR_MODE_SRK || + (ctx->mode == TPM2_PROTECTOR_MODE_NV && TPM_HT_IS_NVINDEX (ctx->nv) == true))) { ctx->srk_type.type = TPM_ALG_ECC; ctx->srk_type.detail.ecc_curve = TPM_ECC_NIST_P256; From 5934bf51cbdf820c71eeed4bbbbcf23756e96217 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Mon, 7 Apr 2025 16:29:21 +0800 Subject: [PATCH 383/402] util/grub-protect: Support NV index mode This commit implements the missing NV index mode support in grub-protect. NV index mode stores the sealed key in the TPM non-volatile memory (NVRAM) instead of a file. There are two supported types of TPM handles. 1. Persistent handle (0x81000000~0x81FFFFFF) Only the raw format is supported due to the limitation of persistent handles. This grub-protect command seals the key into the persistent handle 0x81000000. # grub-protect \ --protector=tpm2 \ --action=add \ --tpm2-bank=sha256 \ --tpm2-pcrs=7,11 \ --tpm2-keyfile=luks-key \ --tpm2-nvindex=0x81000000 2. NV index handle (0x1000000~0x1FFFFFF) Both TPM 2.0 Key File format and the raw format are supported by NV index handles. Here is the grub-protect command to seal the key in TPM 2.0 Key File format into the NV index handle 0x1000000. # grub-protect \ --protector=tpm2 \ --action=add \ --tpm2key \ --tpm2-bank=sha256 \ --tpm2-pcrs=7,11 \ --tpm2-keyfile=luks-key \ --tpm2-nvindex=0x1000000 Besides the "add" action, the corresponding "remove" action is also introduced. To remove the data from a persistent or NV index handle, just use "--tpm2-nvindex=HANDLE" combining with "--tpm2-evict". This sample command removes the data from the NV index handle 0x1000000. # grub-protect \ --protector=tpm2 \ --action=remove \ --tpm2-evict \ --tpm2-nvindex=0x1000000 Also set and check the boolean variables with true/false instead of 1/0. Signed-off-by: Gary Lin Reviewed-by: Stefan Berger Reviewed-by: Daniel Kiper --- util/grub-protect.c | 343 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 287 insertions(+), 56 deletions(-) diff --git a/util/grub-protect.c b/util/grub-protect.c index 5b7e952f4..40d4a3fc5 100644 --- a/util/grub-protect.c +++ b/util/grub-protect.c @@ -61,7 +61,8 @@ typedef enum protect_opt PROTECT_OPT_TPM2_KEYFILE, PROTECT_OPT_TPM2_OUTFILE, PROTECT_OPT_TPM2_EVICT, - PROTECT_OPT_TPM2_TPM2KEY + PROTECT_OPT_TPM2_TPM2KEY, + PROTECT_OPT_TPM2_NVINDEX, } protect_opt_t; /* Option flags to keep track of specified arguments */ @@ -79,7 +80,8 @@ typedef enum protect_arg PROTECT_ARG_TPM2_KEYFILE = 1 << 7, PROTECT_ARG_TPM2_OUTFILE = 1 << 8, PROTECT_ARG_TPM2_EVICT = 1 << 9, - PROTECT_ARG_TPM2_TPM2KEY = 1 << 10 + PROTECT_ARG_TPM2_TPM2KEY = 1 << 10, + PROTECT_ARG_TPM2_NVINDEX = 1 << 11 } protect_arg_t; typedef enum protect_protector @@ -111,6 +113,7 @@ typedef struct protect_args const char *tpm2_outfile; bool tpm2_evict; bool tpm2_tpm2key; + TPM_HANDLE_t tpm2_nvindex; } protect_args_t; static struct argp_option protect_options[] = @@ -224,6 +227,15 @@ static struct argp_option protect_options[] = N_("Use TPM 2.0 Key File format."), .group = 0 }, + { + .name = "tpm2-nvindex", + .key = PROTECT_OPT_TPM2_NVINDEX, + .arg = "NUM", + .flags = 0, + .doc = + N_("Store the sealed key in a persistent or NV index handle."), + .group = 0 + }, /* End of list */ { 0, 0, 0, 0, 0, 0 } }; @@ -668,8 +680,8 @@ extern asn1_static_node tpm2key_asn1_tab[]; #define TPM2KEY_SEALED_KEY_OID "2.23.133.10.1.5" static grub_err_t -protect_tpm2_export_tpm2key (const protect_args_t *args, - tpm2_sealed_key_t *sealed_key) +protect_tpm2_export_tpm2key (const protect_args_t *args, tpm2_sealed_key_t *sealed_key, + void **der_buf, int *der_buf_size) { const char *sealed_key_oid = TPM2KEY_SEALED_KEY_OID; asn1_node asn1_def = NULL; @@ -689,12 +701,13 @@ protect_tpm2_export_tpm2key (const protect_args_t *args, }; struct grub_tpm2_buffer pub_buf; struct grub_tpm2_buffer priv_buf; - void *der_buf = NULL; - int der_buf_size = 0; int i; int ret; grub_err_t err; + if (der_buf == NULL) + return GRUB_ERR_BAD_ARGUMENT; + for (i = 0; i < args->tpm2_pcr_count; i++) TPMS_PCR_SELECTION_SelectPCR (&pcr_sel.pcrSelections[0], args->tpm2_pcrs[i]); @@ -844,8 +857,8 @@ protect_tpm2_export_tpm2key (const protect_args_t *args, } /* Create the DER binary */ - der_buf_size = 0; - ret = asn1_der_coding (tpm2key, "", NULL, &der_buf_size, NULL); + *der_buf_size = 0; + ret = asn1_der_coding (tpm2key, "", NULL, der_buf_size, NULL); if (ret != ASN1_MEM_ERROR) { fprintf (stderr, "Failed to get DER size: 0x%x\n", ret); @@ -853,15 +866,15 @@ protect_tpm2_export_tpm2key (const protect_args_t *args, goto error; } - der_buf = grub_malloc (der_buf_size); - if (der_buf == NULL) + *der_buf = grub_malloc (*der_buf_size); + if (*der_buf == NULL) { fprintf (stderr, "Failed to allocate memory for DER encoding\n"); err = GRUB_ERR_OUT_OF_MEMORY; goto error; } - ret = asn1_der_coding (tpm2key, "", der_buf, &der_buf_size, NULL); + ret = asn1_der_coding (tpm2key, "", *der_buf, der_buf_size, NULL); if (ret != ASN1_SUCCESS) { fprintf (stderr, "DER coding error: 0x%x\n", ret); @@ -869,13 +882,7 @@ protect_tpm2_export_tpm2key (const protect_args_t *args, goto error; } - err = protect_write_file (args->tpm2_outfile, der_buf, der_buf_size); - if (err != GRUB_ERR_NONE) - fprintf (stderr, N_("Could not write tpm2key file (%s).\n"), strerror (errno)); - error: - grub_free (der_buf); - if (tpm2key) asn1_delete_structure (&tpm2key); @@ -883,10 +890,8 @@ protect_tpm2_export_tpm2key (const protect_args_t *args, } static grub_err_t -protect_tpm2_export_sealed_key (const char *filepath, - tpm2_sealed_key_t *sealed_key) +protect_tpm2_export_raw (tpm2_sealed_key_t *sealed_key, void **out_buf, int *out_buf_size) { - grub_err_t err; struct grub_tpm2_buffer buf; grub_tpm2_buffer_init (&buf); @@ -896,13 +901,98 @@ protect_tpm2_export_sealed_key (const char *filepath, if (buf.error != 0) return GRUB_ERR_BAD_ARGUMENT; - err = protect_write_file (filepath, buf.data, buf.size); - if (err != GRUB_ERR_NONE) - fprintf (stderr, N_("Could not write sealed key file (%s).\n"), strerror (errno)); + *out_buf_size = buf.size; + *out_buf = grub_malloc (buf.size); + + if (*out_buf == NULL) + { + fprintf (stderr, N_("Could not allocate memory for the raw format key.\n")); + return GRUB_ERR_OUT_OF_MEMORY; + } + + grub_memcpy (*out_buf, buf.data, buf.size); + + return GRUB_ERR_NONE; +} + +static grub_err_t +protect_tpm2_export_persistent (protect_args_t *args, + TPM_HANDLE_t srk_handle, + tpm2_sealed_key_t *sealed_key) +{ + TPMS_AUTH_COMMAND_t authCmd = {0}; + TPM2B_NAME_t name = {0}; + TPM_HANDLE_t sealed_handle; + TPM_RC_t rc; + grub_err_t err = GRUB_ERR_NONE; + + /* Load the sealed key and associate it with the SRK */ + authCmd.sessionHandle = TPM_RS_PW; + rc = grub_tpm2_load (srk_handle, &authCmd, &sealed_key->private, &sealed_key->public, + &sealed_handle, &name, NULL); + if (rc != TPM_RC_SUCCESS) + { + fprintf (stderr, "Failed to load sealed key (TPM2_Load: %x).\n", rc); + return GRUB_ERR_BAD_DEVICE; + } + + /* Make the sealed key object persistent */ + authCmd.sessionHandle = TPM_RS_PW; + rc = grub_tpm2_evictcontrol (TPM_RH_OWNER, sealed_handle, &authCmd, args->tpm2_nvindex, NULL); + if (rc != TPM_RC_SUCCESS) + { + fprintf (stderr, "Failed to make sealed key persistent with handle 0x%x (TPM2_EvictControl: 0x%x).\n", args->tpm2_nvindex, rc); + err = GRUB_ERR_BAD_DEVICE; + goto exit; + } + + exit: + grub_tpm2_flushcontext (sealed_handle); return err; } +static grub_err_t +protect_tpm2_export_nvindex (protect_args_t *args, void *data, int data_size) +{ + TPMS_AUTH_COMMAND_t authCmd = {0}; + TPM2B_NV_PUBLIC_t pub_info = {0}; + TPM2B_MAX_NV_BUFFER_t nv_data = {0}; + TPM_RC_t rc; + + if (data_size > TPM_MAX_NV_BUFFER_SIZE || data_size < 0) + { + fprintf (stderr, N_("Invalid tpm2key size for TPM NV buffer\n")); + return GRUB_ERR_OUT_OF_RANGE; + } + + pub_info.nvPublic.nvIndex = args->tpm2_nvindex; + pub_info.nvPublic.nameAlg = TPM_ALG_SHA256; + pub_info.nvPublic.attributes = TPMA_NV_OWNERWRITE | TPMA_NV_OWNERREAD; + pub_info.nvPublic.dataSize = (grub_uint16_t) data_size; + + authCmd.sessionHandle = TPM_RS_PW; + rc = grub_tpm2_nv_definespace (TPM_RH_OWNER, &authCmd, NULL, &pub_info); + if (rc != TPM_RC_SUCCESS) + { + fprintf (stderr, "Failed to define NV space for 0x%x (TPM2_NV_DefineSpace: 0x%x)\n", args->tpm2_nvindex, rc); + return GRUB_ERR_BAD_DEVICE; + } + + authCmd.sessionHandle = TPM_RS_PW; + grub_memcpy (nv_data.buffer, data, data_size); + nv_data.size = (grub_uint16_t) data_size; + + rc = grub_tpm2_nv_write (TPM_RH_OWNER, args->tpm2_nvindex, &authCmd, &nv_data, 0); + if (rc != TPM_RC_SUCCESS) + { + fprintf (stderr, "Failed to write data into 0x%x (TPM2_NV_Write: 0x%x)\n", args->tpm2_nvindex, rc); + return GRUB_ERR_BAD_DEVICE; + } + + return GRUB_ERR_NONE; +} + static grub_err_t protect_tpm2_add (protect_args_t *args) { @@ -911,6 +1001,8 @@ protect_tpm2_add (protect_args_t *args) grub_size_t key_size; TPM_HANDLE_t srk; TPM2B_DIGEST_t policy_digest; + void *out_buf = NULL; + int out_buf_size; tpm2_sealed_key_t sealed_key; err = protect_tpm2_open_device (args->tpm2_device); @@ -940,15 +1032,51 @@ protect_tpm2_add (protect_args_t *args) if (err != GRUB_ERR_NONE) goto exit3; - if (args->tpm2_tpm2key != 0) - err = protect_tpm2_export_tpm2key (args, &sealed_key); + if (args->tpm2_tpm2key == true) + { + err = protect_tpm2_export_tpm2key (args, &sealed_key, &out_buf, &out_buf_size); + if (err != GRUB_ERR_NONE) + { + fprintf (stderr, N_("Could not export to TPM 2.0 Key File format\n")); + goto exit3; + } + } else - err = protect_tpm2_export_sealed_key (args->tpm2_outfile, &sealed_key); - if (err != GRUB_ERR_NONE) - goto exit3; + { + err = protect_tpm2_export_raw (&sealed_key, &out_buf, &out_buf_size); + if (err != GRUB_ERR_NONE) + { + fprintf (stderr, N_("Could not export to the raw format\n")); + goto exit3; + } + } + + if (args->tpm2_outfile != NULL) + { + err = protect_write_file (args->tpm2_outfile, out_buf, out_buf_size); + if (err != GRUB_ERR_NONE) + { + fprintf (stderr, N_("Could not write key file (%s).\n"), strerror (errno)); + goto exit3; + } + } + + if (TPM_HT_IS_NVINDEX (args->tpm2_nvindex) == true) + { + err = protect_tpm2_export_nvindex (args, out_buf, out_buf_size); + if (err != GRUB_ERR_NONE) + goto exit3; + } + else if (TPM_HT_IS_PERSISTENT (args->tpm2_nvindex) == true) + { + err = protect_tpm2_export_persistent (args, srk, &sealed_key); + if (err != GRUB_ERR_NONE) + goto exit3; + } exit3: grub_tpm2_flushcontext (srk); + grub_free (out_buf); exit2: grub_free (key); @@ -960,14 +1088,66 @@ protect_tpm2_add (protect_args_t *args) } static grub_err_t -protect_tpm2_remove (protect_args_t *args) +protect_tpm2_evict (TPM_HANDLE_t handle) { TPM_RC_t rc; TPM2B_PUBLIC_t public; - TPMS_AUTH_COMMAND_t authCommand = {0}; + TPMS_AUTH_COMMAND_t authCmd = {0}; + + /* Find the persistent handle */ + rc = grub_tpm2_readpublic (handle, NULL, &public); + if (rc != TPM_RC_SUCCESS) + { + fprintf (stderr, "Handle 0x%x not found.\n", handle); + return GRUB_ERR_BAD_ARGUMENT; + } + + /* Evict the persistent handle */ + authCmd.sessionHandle = TPM_RS_PW; + rc = grub_tpm2_evictcontrol (TPM_RH_OWNER, handle, &authCmd, handle, NULL); + if (rc != TPM_RC_SUCCESS) + { + fprintf (stderr, "Failed to evict handle 0x%x (TPM2_EvictControl: 0x%x).\n", handle, rc); + return GRUB_ERR_BAD_DEVICE; + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +protect_tpm2_nv_undefine (TPM_HANDLE_t handle) +{ + TPM_RC_t rc; + TPM2B_NV_PUBLIC_t nv_public; + TPMS_AUTH_COMMAND_t authCmd = {0}; + TPM2B_NAME_t nv_name; + + /* Find the nvindex handle */ + rc = grub_tpm2_nv_readpublic (handle, NULL, &nv_public, &nv_name); + if (rc != TPM_RC_SUCCESS) + { + fprintf (stderr, "Handle 0x%x not found.\n", handle); + return GRUB_ERR_BAD_ARGUMENT; + } + + /* Undefine the nvindex handle */ + authCmd.sessionHandle = TPM_RS_PW; + rc = grub_tpm2_nv_undefinespace (TPM_RH_OWNER, handle, &authCmd); + if (rc != TPM_RC_SUCCESS) + { + fprintf (stderr, "Failed to undefine handle 0x%x (TPM2_NV_UndefineSpace: 0x%x).\n", handle, rc); + return GRUB_ERR_BAD_DEVICE; + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +protect_tpm2_remove (protect_args_t *args) +{ grub_err_t err; - if (args->tpm2_evict == 0) + if (args->tpm2_evict == false) { printf ("--tpm2-evict not specified, nothing to do.\n"); return GRUB_ERR_NONE; @@ -977,35 +1157,41 @@ protect_tpm2_remove (protect_args_t *args) if (err != GRUB_ERR_NONE) return err; - /* Find SRK */ - rc = grub_tpm2_readpublic (args->tpm2_srk, NULL, &public); - if (rc != TPM_RC_SUCCESS) + if (args->tpm2_srk != 0) { - fprintf (stderr, "SRK with handle 0x%x not found.\n", args->tpm2_srk); - err = GRUB_ERR_BAD_ARGUMENT; - goto exit1; + err = protect_tpm2_evict (args->tpm2_srk); + if (err != GRUB_ERR_NONE) + goto exit; } - /* Evict SRK */ - authCommand.sessionHandle = TPM_RS_PW; - - rc = grub_tpm2_evictcontrol (TPM_RH_OWNER, args->tpm2_srk, &authCommand, args->tpm2_srk, NULL); - if (rc != TPM_RC_SUCCESS) + if (args->tpm2_nvindex != 0) { - fprintf (stderr, "Failed to evict SRK with handle 0x%x (TPM2_EvictControl: 0x%x).\n", args->tpm2_srk, rc); - err = GRUB_ERR_BAD_DEVICE; - goto exit2; + if (TPM_HT_IS_PERSISTENT (args->tpm2_nvindex) == true) + { + err = protect_tpm2_evict (args->tpm2_nvindex); + if (err != GRUB_ERR_NONE) + goto exit; + } + else if (TPM_HT_IS_NVINDEX (args->tpm2_nvindex) == true) + { + err = protect_tpm2_nv_undefine (args->tpm2_nvindex); + if (err != GRUB_ERR_NONE) + goto exit; + } + else + { + fprintf (stderr, "Unsupported handle 0x%x\n", args->tpm2_nvindex); + err = GRUB_ERR_BAD_ARGUMENT; + goto exit; + } } err = GRUB_ERR_NONE; - exit2: - grub_tpm2_flushcontext (args->tpm2_srk); - - exit1: + exit: protect_tpm2_close_device (); - return GRUB_ERR_NONE; + return err; } static grub_err_t @@ -1045,9 +1231,36 @@ protect_tpm2_args_verify (protect_args_t *args) return GRUB_ERR_BAD_ARGUMENT; } - if (args->tpm2_outfile == NULL) + if (args->tpm2_outfile == NULL && args->tpm2_nvindex == 0) { - fprintf (stderr, N_("--tpm2-outfile must be specified.\n")); + fprintf (stderr, N_("--tpm2-outfile or --tpm2-nvindex must be specified.\n")); + return GRUB_ERR_BAD_ARGUMENT; + } + + if (args->tpm2_nvindex != 0) + { + if (args->tpm2_tpm2key == true && TPM_HT_IS_PERSISTENT (args->tpm2_nvindex) == true) + { + fprintf (stderr, N_("Persistent handle does not support TPM 2.0 Key File format.\n")); + return GRUB_ERR_BAD_ARGUMENT; + } + + if (TPM_HT_IS_PERSISTENT (args->tpm2_nvindex) == false && TPM_HT_IS_NVINDEX (args->tpm2_nvindex) == false) + { + fprintf (stderr, N_("--tpm2-nvindex must be a persistent or NV index handle.\n")); + return GRUB_ERR_BAD_ARGUMENT; + } + + if (args->tpm2_nvindex == args->tpm2_srk) + { + fprintf (stderr, N_("--tpm2-nvindex and --tpm2-srk must be different.\n")); + return GRUB_ERR_BAD_ARGUMENT; + } + } + + if (args->tpm2_srk != 0 && TPM_HT_IS_PERSISTENT(args->tpm2_srk) == false) + { + fprintf (stderr, N_("--tpm2-srk must be a persistent handle, e.g. 0x81000000.\n")); return GRUB_ERR_BAD_ARGUMENT; } @@ -1099,9 +1312,9 @@ protect_tpm2_args_verify (protect_args_t *args) return GRUB_ERR_BAD_ARGUMENT; } - if (args->tpm2_srk == 0) + if (args->tpm2_srk == 0 && args->tpm2_nvindex == 0) { - fprintf (stderr, N_("--tpm2-srk is not specified when --action is 'remove'.\n")); + fprintf (stderr, N_("missing --tpm2-srk or --tpm2-nvindex for --action 'remove'.\n")); return GRUB_ERR_BAD_ARGUMENT; } @@ -1274,7 +1487,7 @@ protect_argp_parser (int key, char *arg, struct argp_state *state) return EINVAL; } - args->tpm2_evict = 1; + args->tpm2_evict = true; args->args |= PROTECT_ARG_TPM2_EVICT; break; @@ -1285,10 +1498,28 @@ protect_argp_parser (int key, char *arg, struct argp_state *state) return EINVAL; } - args->tpm2_tpm2key = 1; + args->tpm2_tpm2key = true; args->args |= PROTECT_ARG_TPM2_TPM2KEY; break; + case PROTECT_OPT_TPM2_NVINDEX: + if (args->args & PROTECT_ARG_TPM2_NVINDEX) + { + fprintf (stderr, N_("--tpm2-nvindex can only be specified once.\n")); + return EINVAL; + } + + err = grub_tpm2_protector_parse_tpm_handle (arg, &args->tpm2_nvindex); + if (err != GRUB_ERR_NONE) + { + if (grub_errno != GRUB_ERR_NONE) + grub_print_error (); + return EINVAL; + } + + args->args |= PROTECT_ARG_TPM2_NVINDEX; + break; + default: return ARGP_ERR_UNKNOWN; } From b7d89e6671161d4e1dfa694355c060345e0cebd9 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Mon, 7 Apr 2025 16:29:22 +0800 Subject: [PATCH 384/402] tests/tpm2_key_protector_test: Simplify the NV index mode test Since grub-protect already supports NV index mode, tpm2_seal_nv() is replaced with one grub-protect command to simplify the test script. "tpm2_evictcontrol" is also replaced with "grub-protect --tpm2-evict". Signed-off-by: Gary Lin Reviewed-by: Stefan Berger Reviewed-by: Daniel Kiper --- tests/tpm2_key_protector_test.in | 108 +++++-------------------------- 1 file changed, 15 insertions(+), 93 deletions(-) diff --git a/tests/tpm2_key_protector_test.in b/tests/tpm2_key_protector_test.in index a92e5f498..07477ba2a 100644 --- a/tests/tpm2_key_protector_test.in +++ b/tests/tpm2_key_protector_test.in @@ -225,105 +225,22 @@ EOF fi } -tpm2_seal_nv () { - keyfile="$1" - nv_index="$2" - pcr_list="$3" - - primary_file=${tpm2testdir}/primary.ctx - session_file=${tpm2testdir}/session.dat - policy_file=${tpm2testdir}/policy.dat - keypub_file=${tpm2testdir}/key.pub - keypriv_file=${tpm2testdir}/key.priv - name_file=${tpm2testdir}/sealing.name - sealing_ctx_file=${tpm2testdir}/sealing.ctx - - # Since we don't run a resource manager on our swtpm instance, it has - # to flush the transient handles after tpm2_createprimary, tpm2_create - # and tpm2_load to avoid the potential out-of-memory (0x902) errors. - # Ref: https://github.com/tpm2-software/tpm2-tools/issues/1338#issuecomment-469689398 - - # Create the primary object - tpm2_createprimary -Q -C o -g sha256 -G ecc -c "${primary_file}" || ret=$? - if [ "${ret}" -ne 0 ]; then - echo "Failed to create the primary object: ${ret}" >&2 - return 1 - fi - tpm2_flushcontext -t || ret=$? - if [ "${ret}" -ne 0 ]; then - echo "Failed to flush the transient handles: ${ret}" >&2 - return 1 - fi - - # Create the policy object - tpm2_startauthsession -S "${session_file}" || ret=$? - if [ "${ret}" -ne 0 ]; then - echo "Failed to start auth session: ${ret}" >&2 - return 1 - fi - tpm2_policypcr -Q -S "${session_file}" -l "${pcr_list}" -L "${policy_file}" || ret=$? - if [ "${ret}" -ne 0 ]; then - echo "Failed to create the policy object: ${ret}" >&2 - return 1 - fi - tpm2_flushcontext "${session_file}" || ret=$? - if [ "${ret}" -ne 0 ]; then - echo "Failed to flush the transient handles: ${ret}" >&2 - return 1 - fi - - # Seal the key into TPM - tpm2_create -Q \ - -C "${primary_file}" \ - -u "${keypub_file}" \ - -r "${keypriv_file}" \ - -L "${policy_file}" \ - -i "${keyfile}" || ret=$? - if [ "${ret}" -ne 0 ]; then - echo "Failed to seal \"${keyfile}\": ${ret}" >&2 - return 1 - fi - tpm2_flushcontext -t || ret=$? - if [ "${ret}" -ne 0 ]; then - echo "Failed to flush the transient handles: ${ret}" >&2 - return 1 - fi - - tpm2_load -Q \ - -C "${primary_file}" \ - -u "${keypub_file}" \ - -r "${keypriv_file}" \ - -n "${name_file}" \ - -c "${sealing_ctx_file}" || ret=$? - if [ "${ret}" -ne 0 ]; then - echo "Failed to load the sealed key into TPM: ${ret}" >&2 - return 1 - fi - tpm2_flushcontext -t || ret=$? - if [ "${ret}" -ne 0 ]; then - echo "Failed to flush the transient handles: ${ret}" >&2 - return 1 - fi - - tpm2_evictcontrol -Q -C o -c "${sealing_ctx_file}" ${nv_index} || ret=$? - if [ "${ret}" -ne 0 ]; then - echo "Failed to store the sealed key into ${nv_index}: ${ret}" >&2 - return 1 - fi - - return 0 -} - tpm2_seal_unseal_nv() { nv_index="0x81000000" - pcr_list="sha256:0,1" grub_cfg=${tpm2testdir}/testcase.cfg # Seal the key into a NV index guarded by PCR 0 and 1 - tpm2_seal_nv "${lukskeyfile}" ${nv_index} ${pcr_list} || ret=$? + grub-protect ${extra_opt} \ + --tpm2-device="${tpm2dev}" \ + --action=add \ + --protector=tpm2 \ + --tpm2-bank=sha256 \ + --tpm2-pcrs=0,1 \ + --tpm2-keyfile="${lukskeyfile}" \ + --tpm2-nvindex="${nv_index}" || ret=$? if [ "${ret}" -ne 0 ]; then - echo "Failed to seal the secret key into ${nv_index}" >&2 + echo "Failed to seal the secret key into ${nv_index}: ${ret}" >&2 return 99 fi @@ -340,7 +257,12 @@ EOF ${grubshell} --timeout=${timeout} --emu-opts="-t ${tpm2dev}" < "${grub_cfg}" > "${testoutput}" || ret=$? # Remove the object from the NV index - tpm2_evictcontrol -Q -C o -c "${nv_index}" || : + grub-protect \ + --tpm2-device="${tpm2dev}" \ + --protector=tpm2 \ + --action=remove \ + --tpm2-nvindex=${nv_index} \ + --tpm2-evict || : if [ "${ret}" -eq 0 ]; then if ! grep -q "^${vtext}$" "${testoutput}"; then From 9f66a4719b7fed24fe36154d634c16dd9bd528c9 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Mon, 7 Apr 2025 16:29:23 +0800 Subject: [PATCH 385/402] tests/tpm2_key_protector_test: Reset "ret" on fail Reset "ret" to 0 when a test case fails so that the other test cases could continue. Also set the exit status to 1 when encountering a failure to reflect the test result. Signed-off-by: Gary Lin Reviewed-by: Stefan Berger Reviewed-by: Daniel Kiper --- tests/tpm2_key_protector_test.in | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/tpm2_key_protector_test.in b/tests/tpm2_key_protector_test.in index 07477ba2a..409696a29 100644 --- a/tests/tpm2_key_protector_test.in +++ b/tests/tpm2_key_protector_test.in @@ -285,12 +285,16 @@ srktests+=("ECC persistent no_fallback_srk") srktests+=("RSA transient fallback_srk") srktests+=("ECC transient fallback_srk") +exit_status=0 + for i in "${!srktests[@]}"; do tpm2_seal_unseal ${srktests[$i]} || ret=$? if [ "${ret}" -eq 0 ]; then echo "TPM2 [${srktests[$i]}]: PASS" elif [ "${ret}" -eq 1 ]; then echo "TPM2 [${srktests[$i]}]: FAIL" + ret=0 + exit_status=1 else echo "Unexpected failure [${srktests[$i]}]" >&2 exit ${ret} @@ -303,9 +307,11 @@ if [ "${ret}" -eq 0 ]; then echo "TPM2 [NV Index]: PASS" elif [ "${ret}" -eq 1 ]; then echo "TPM2 [NV Index]: FAIL" + ret=0 + exit_status=1 else echo "Unexpected failure [NV index]" >&2 exit ${ret} fi -exit 0 +exit ${exit_status} From 2043b6899b20e32bb74458689a5e5bf6d8144f63 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Mon, 7 Apr 2025 16:29:24 +0800 Subject: [PATCH 386/402] tests/tpm2_key_protector_test: Add more NV index mode tests Two more NV index test cases are added to test key sealing and unsealing with the NV index handle 0x1000000. Signed-off-by: Gary Lin Reviewed-by: Stefan Berger Reviewed-by: Daniel Kiper --- tests/tpm2_key_protector_test.in | 57 ++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/tests/tpm2_key_protector_test.in b/tests/tpm2_key_protector_test.in index 409696a29..fae27f9e4 100644 --- a/tests/tpm2_key_protector_test.in +++ b/tests/tpm2_key_protector_test.in @@ -226,7 +226,23 @@ EOF } tpm2_seal_unseal_nv() { - nv_index="0x81000000" + handle_type="$1" + key_type="$2" + + extra_opt="" + extra_grub_opt="" + + if [ "$handle_type" = "nvindex" ]; then + nv_index="0x1000000" + else + nv_index="0x81000000" + fi + + if [ "$key_type" = "tpm2key" ]; then + extra_opt="--tpm2key" + else + extra_grub_opt="--pcrs=0,1" + fi grub_cfg=${tpm2testdir}/testcase.cfg @@ -247,7 +263,7 @@ tpm2_seal_unseal_nv() { # Write the TPM unsealing script cat > ${grub_cfg} <&2 + echo "Unexpected failure [SRK][${srktests[$i]}]" >&2 exit ${ret} fi done -# Testcase for NV index mode -tpm2_seal_unseal_nv || ret=$? -if [ "${ret}" -eq 0 ]; then - echo "TPM2 [NV Index]: PASS" -elif [ "${ret}" -eq 1 ]; then - echo "TPM2 [NV Index]: FAIL" - ret=0 - exit_status=1 -else - echo "Unexpected failure [NV index]" >&2 - exit ${ret} -fi +# Testcases for NV index mode +declare -a nvtests=() +nvtests+=("persistent raw") +nvtests+=("nvindex raw") +nvtests+=("nvindex tpm2key") + +for i in "${!nvtests[@]}"; do + tpm2_seal_unseal_nv ${nvtests[$i]} || ret=$? + if [ "${ret}" -eq 0 ]; then + echo "TPM2 [NV Index][${nvtests[$i]}]: PASS" + elif [ "${ret}" -eq 1 ]; then + echo "TPM2 [NV Index][${nvtests[$i]}]: FAIL" + ret=0 + exit_status=1 + else + echo "Unexpected failure [NV index][${nvtests[$i]}]" >&2 + exit ${ret} + fi +done exit ${exit_status} From 9d4b382aa0ea161461f920b8d0c5f656285ca93d Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Mon, 7 Apr 2025 16:29:25 +0800 Subject: [PATCH 387/402] docs: Update NV index mode of TPM2 key protector This commit updates the NV index mode section and the grub-protect section to reflect the recent changes in TPM2 key protector and grub-protect. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper --- docs/grub.texi | 198 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 171 insertions(+), 27 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index ae89838bc..cf64c54c8 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -9142,38 +9142,38 @@ When/After the shim or GRUB are updated, it only requires to run the last @subsection NV index mode Instead of storing the sealed key in a file, NV index mode uses the TPM -non-volatile memory to store the sealed key. +non-volatile memory to store the sealed key and could be useful when accessing +the file is not possible. -The following sample commands use tpm2-tools (@url{https://github.com/tpm2-software/tpm2-tools}) -commands to seal @file{luks.key} into the specific NV index: @kbd{0x81000000}. +However, the Linux root user must be careful who she/he gives access to the +TPM (tss group) since those users will also be able to modify the NV index +that's holding the key. -First, we need to create the object file for the primary key, i.e. storage -root key (SRK) with the default key settings in GRUB: SHA256 hash algorithm -and ECC key algorithm. +There are two types of TPM handles supported by NV index mode: persistent +handle and NV index handle. + +@subsubsection Persistent handle + +The range of persistent handles is from @kbd{0x81000000} to @kbd{0x81FFFFFF}. +The persistent handle is designed to make TPM objects persistent through +power cycles, and only TPM objects, such as RSA or EC keys, are accepted. +Thus, only the raw format is supported by persistent handles. The following +shows the @command{grub-protect} command to seal the disk key @file{luks.key} +into the persistent handle @kbd{0x81000000} with the PCRs @kbd{0,2,4,7}. @example -# @kbd{tpm2_createprimary -C o -g sha256 -G ecc -c primary.ctx} +@group +# @kbd{grub-protect \ + --protector=tpm2 \ + --action=add \ + --tpm2-bank=sha256 \ + --tpm2-pcrs=0,2,4,7 \ + --tpm2-keyfile=luks.key \ + --tpm2-nvindex=0x81000000} +@end group @end example -The next commands collect the current values of PCR 0, 2, 4, and 7 and saves -them in @file{pcr.dat}. - -@example -# @kbd{tpm2_startauthsession -S session.dat} -# @kbd{tpm2_policypcr -S session.dat -l sha256:0,2,4,7 -f pcrs.dat -L policy.dat} -# @kbd{tpm2_flushcontext session.dat} -@end example - -The last commands seal @file{luks.key} with the primary key and stores the -result in @kbd{0x81000000}. - -@example -# @kbd{cat luks.key | tpm2_create -C primary.ctx -u key.pub -r key.priv -L policy.dat -i-} -# @kbd{tpm2_load -C primary.ctx -u key.pub -r key.priv -n sealing.name -c sealing.ctx} -# @kbd{tpm2_evictcontrol -C o -c sealing.ctx 0x81000000} -@end example - -To unseal the key, we have to specify the mode @kbd{nv}, the NV index +To unseal the key, we have to specify the mode @kbd{nv}, the persistent handle @kbd{0x81000000}, and the PCRs @kbd{0,2,4,7} for the @command{tpm2_key_protector_init} command. @@ -9182,6 +9182,80 @@ grub> @kbd{tpm2_key_protector_init --mode=nv --nvindex=0x81000000 --pcrs=0,2,4,7 grub> @kbd{cryptomount -u --protector tpm2} @end example +If the key in the persistent handle becomes unwanted, the following +@command{grub-protect} command removes the specified persistent handle +@kbd{0x81000000}. + +@example +@group +# @kbd{grub-protect \ + --protector=tpm2 \ + --action=remove \ + --tpm2-evict \ + --tpm2-nvindex=0x81000000} +@end group +@end example + +@subsubsection NV index handle + +The range of NV index handles is from @kbd{0x1000000} to @kbd{0x1FFFFFF}. +Unlike the persistent handle, the NV index handle allows user-defined data, +so it can easily support both the TPM 2.0 Key File format as well as the raw +format. + +The following @kbd{grub-protect} command seals the disk key @file{luks.key} +into the NV index handle @kbd{0x1000000} with the PCRs @kbd{0,2,4,7} while +using the TPM 2.0 Key File format. + +@example +@group +# @kbd{grub-protect \ + --protector=tpm2 \ + --action=add \ + --tpm2key \ + --tpm2-bank=sha256 \ + --tpm2-pcrs=0,2,4,7 \ + --tpm2-keyfile=luks.key \ + --tpm2-nvindex=0x1000000} +@end group +@end example + +Furthermore, it is also possible to insert an existing key file, +@file{sealed.tpm}, into a specific NV index handle using the following +tpm2-tools (@url{https://github.com/tpm2-software/tpm2-tools}) commands. + +@example +@group +# @kbd{tpm2_nvdefine -C o \ + -a "ownerread|ownerwrite" \ + -s $(stat -c %s sealed.tpm) \ + 0x1000000} +@end group +# @kbd{tpm2_nvwrite -C o -i sealed.tpm 0x1000000} +@end example + +When unsealing the key in TPM 2.0 Key File format, only the mode @kbd{nv} +and the NV index handle @kbd{0x1000000} have to be specified for the +@command{tpm2_key_protector_init} command. + +@example +grub> @kbd{tpm2_key_protector_init --mode=nv --nvindex=0x1000000} +grub> @kbd{cryptomount -u --protector tpm2} +@end example + +The following @command{grub-protect} command allows to remove the specified +NV index handle @kbd{0x1000000}. + +@example +@group +# @kbd{grub-protect \ + --protector=tpm2 \ + --action=remove \ + --tpm2-evict \ + --tpm2-nvindex=0x1000000} +@end group +@end example + @subsection Setting up software TPM for EMU platform In order to test TPM2 key protector and TPM2 Software Stack (TSS2), it is @@ -10136,11 +10210,16 @@ unsealing. (default: @samp{7}) @item --tpm2-srk=@var{handle} Set the SRK handle, e.g. @samp{0x81000000}, if the SRK is to be made persistent. +@item --tpm2-nvindex=@var{handle} +Set the handle, e.g. @samp{0x81000000} or @samp{0x1000000}, for NV index mode. + @item --tpm2key Use TPM 2.0 Key File format. @end table +@subsection 'Add' action + Before sealing the key, please check the TPM PCR usage (@pxref{TPM2 key protector, TPM PCR usage}) to choose a proper set of PCRs. @@ -10166,12 +10245,77 @@ grub> @kbd{tpm2_key_protector_init -T (hd0,gpt1)/efi/grub/sealed.tpm} grub> @kbd{cryptomount -u -P tpm2} @end example +Besides writing the PCR-sealed key into a file, @command{grub-protect} can +write the sealed key into TPM non-volatile memory. Here is the +@command{grub-protect} command to write the sealed key into the NV index +handle @samp{0x1000000}. + +@example +@group +# @kbd{grub-protect --action=add \ + --protector=tpm2 \ + --tpm2-pcrs=0,2,4,7 \ + --tpm2key \ + --tpm2-keyfile=luks.key \ + --tpm2-nvindex=0x1000000} +@end group +@end example + +Later, GRUB can fetch the key from @samp{0x1000000}. + +@example +grub> @kbd{tpm2_key_protector_init --mode=nv --nvindex=0x1000000} +grub> @kbd{cryptomount -u -P tpm2} +@end example + In most of cases, the user only needs to create the key with the `add' action. If auto-unlocking is unwanted, just remove the file and the @command{tpm2_key_protector_init} command and invoke the @command{cryptomount} command without @kbd{-P tpm2}. -The only use case for the `remove' action is when the SRK is made persistent. +@subsection 'Remove' action + +The `remove' action is used to remove the handles for NV index mode and the +persistent SRK. + +@subsubsection Handles for NV index mode + +There are two types of TPM handles supported by NV index mode: persistent +handles and NV index handles, and @command{tpm2_getcap} can be used to +check the existing handles. + +To display the list of existing persistent handles: + +@example +@group +# @kbd{tpm2_getcap handles-persistent} +- 0x81000000 +@end group +@end example + +Similarly, to display the list of existing NV index handles: + +@example +@group +# @kbd{tpm2_getcap handles-nv-index} +- 0x1000000 +@end group +@end example + +If the sealed key at an NV index handle is not needed anymore, the user can +remove the handle with @kbd{--tpm2-nvindex} and @kbd{--tpm2-evict}. For +example, this command removes the data from NV index @samp{0x1000000}: + +@example +@group +# @kbd{grub-protect --action=remove \ + --protector=tpm2 \ + --tpm2-evict \ + --tpm2-nvindex 0x1000000} \ +@end group +@end example + +@subsubsection Persistent SRK There are two supported SRKs in @command{grub-protect}: @samp{RSA} and @samp{ECC}. Due to slower key generation, some users of the @samp{RSA} SRK may prefer From 6252eb97ca54cb5865f0d9d531a5b06cbea5bf95 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Mon, 7 Apr 2025 16:29:26 +0800 Subject: [PATCH 388/402] INSTALL: Document the packages needed for TPM2 key protector tests The TPM2 key protector tests require two external packages: swtpm-tools and tpm2-tools. Add those two packages to the INSTALL file to inform the user to install those packages before starting the TPM2 key protector tests. Signed-off-by: Gary Lin Reviewed-by: Daniel Kiper Reviewed-by: Stefan Berger --- INSTALL | 1 + 1 file changed, 1 insertion(+) diff --git a/INSTALL b/INSTALL index 6b04e3016..724584c57 100644 --- a/INSTALL +++ b/INSTALL @@ -74,6 +74,7 @@ Prerequisites for make-check: * wamerican, for grub-fs-tester * mtools, FAT tools for EFI platforms * xfonts-unifont, for the functional tests +* swtpm-tools and tpm2-tools, for TPM2 key protector tests * If running a Linux kernel the following modules must be loaded: - fuse, loop From 1fe0948558258dbdd371cf2df8ccc993699fb76a Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Mon, 7 Apr 2025 16:29:27 +0800 Subject: [PATCH 389/402] docs: Document the long options of tpm2_key_protect_init Add the long options of tpm2_key_protect_init along with the short options. Signed-off-by: Gary Lin 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 cf64c54c8..2b3d536d3 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -8055,7 +8055,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{-m} mode] | [@option{-p} pcrlist] | [@option{-b} pcrbank] | [ [@option{-T} tpm2key_file] | [@option{-k} keyfile] ] | [@option{-s} handle] | [@option{-a} srk_type] | [@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{--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 From 8fef533cf69547576f522b0e563c9c6fee635ece Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Thu, 27 Mar 2025 19:32:23 +0300 Subject: [PATCH 390/402] configure: Add -mno-relax on riscv* Without this option compiler sometimes emits R_RISCV_ALIGN relocs. Unlike other relocs this one requires the linker to do NOP deletions and we can't ignore them. Just instruct compiler not to emit them. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- configure.ac | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/configure.ac b/configure.ac index ad1e7bea5..83e3ddf90 100644 --- a/configure.ac +++ b/configure.ac @@ -913,6 +913,31 @@ if test "x$target_cpu" = xloongarch64; then TARGET_CCASFLAGS="$TARGET_CCASFLAGS -Wa,-mla-global-with-abs" fi +if test "x$target_cpu" = xriscv64 || test "x$target_cpu" = xriscv32; then + AC_CACHE_CHECK([for no-relax options], grub_cv_target_cc_mno_relax, [ + grub_cv_target_cc_mno_relax=no + for cand in "-mno-relax" "-Wa,-mno-relax"; do + if test x"$grub_cv_target_cc_mno_relax" != xno ; then + break + fi + CFLAGS="$TARGET_CFLAGS $cand -Werror" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + asm (".globl start; start:"); + void __main (void); + void __main (void) {} + int main (void); + ]], [[]])], [grub_cv_target_cc_mno_relax="$cand"], []) + done + ]) + + CFLAGS="$TARGET_CFLAGS" + + if test x"$grub_cv_target_cc_mno_relax" != xno ; then + TARGET_CFLAGS="$TARGET_CFLAGS $grub_cv_target_cc_mno_relax" + TARGET_CCASFLAGS="$TARGET_CCASFLAGS $grub_cv_target_cc_mno_relax" + fi +fi + # GRUB doesn't use float or doubles at all. Yet some toolchains may decide # that floats are a good fit to run instead of what's written in the code. # Given that floating point unit is disabled (if present to begin with) From 6504a8d4bdd3f8d3a8541b3eee69dce24ad986df Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Sat, 5 Apr 2025 20:45:23 +0300 Subject: [PATCH 391/402] lib/datetime: Specify license in emu module Other platforms specify license in platform-specific files but corresponding code for emu is in kernel, so datetime ends up without license section. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/lib/datetime.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/grub-core/lib/datetime.c b/grub-core/lib/datetime.c index 9120128ca..8f0922fb0 100644 --- a/grub-core/lib/datetime.c +++ b/grub-core/lib/datetime.c @@ -21,6 +21,11 @@ #include #include #include +#ifdef GRUB_MACHINE_EMU +#include + +GRUB_MOD_LICENSE ("GPLv3+"); +#endif static const char *const grub_weekday_names[] = { From 223fcf808424227b85969972931d7da3cd71fe9a Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Mon, 7 Apr 2025 22:05:40 +0300 Subject: [PATCH 392/402] loader/ia64/efi/linux: Reset grub_errno on failure to allocate The code goes on to allocate memory in another region on failure, hence it should discard the error. Signed-off-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/loader/ia64/efi/linux.c | 1 + 1 file changed, 1 insertion(+) diff --git a/grub-core/loader/ia64/efi/linux.c b/grub-core/loader/ia64/efi/linux.c index 3dd2e8236..99cb244d8 100644 --- a/grub-core/loader/ia64/efi/linux.c +++ b/grub-core/loader/ia64/efi/linux.c @@ -383,6 +383,7 @@ grub_load_elf64 (grub_file_t file, void *buffer, const char *filename) { kernel_mem = grub_efi_allocate_fixed (low_addr, kernel_pages); reloc_offset = 0; + grub_errno = GRUB_ERR_NONE; } /* Try to relocate. */ if (! kernel_mem && (!relocate || grub_strcmp (relocate, "off") != 0)) From a4da71dafeea519b034beb159dfe80c486c2107c Mon Sep 17 00:00:00 2001 From: Egor Ignatov Date: Thu, 10 Apr 2025 10:55:02 +0300 Subject: [PATCH 393/402] util/grub-install: Include raid5rec module for RAID 4 as well RAID 4 requires the same recovery module as RAID 5. Extend the condition to cover both RAID levels. Signed-off-by: Egor Ignatov Reviewed-by: Daniel Kiper --- util/grub-install.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/grub-install.c b/util/grub-install.c index 7dc5657bb..060246589 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -459,7 +459,7 @@ probe_mods (grub_disk_t disk) if (disk->dev->disk_raidname) grub_install_push_module (disk->dev->disk_raidname (disk)); } - if (raid_level == 5) + if (raid_level == 4 || raid_level == 5) grub_install_push_module ("raid5rec"); if (raid_level == 6) grub_install_push_module ("raid6rec"); From 4abac0ad5a7914dd3cdfff08aaac06588bf98d80 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Wed, 4 Dec 2024 07:50:28 -0600 Subject: [PATCH 394/402] fs/xfs: Fix large extent counters incompat feature support When large extent counter / NREXT64 support was added to GRUB, it missed a couple of direct reads of nextents which need to be changed to the new NREXT64-aware helper as well. Without this, we'll have mis-reads of some directories with this feature enabled. The large extent counter fix likely raced on merge with commit 07318ee7e (fs/xfs: Fix XFS directory extent parsing) which added the new direct nextents reads just prior, causing this issue. Fixes: aa7c1322671e (fs/xfs: Add large extent counters incompat feature support) Signed-off-by: Eric Sandeen Reviewed-by: Anthony Iliopoulos Reviewed-by: Jon DeVree Reviewed-by: Vladimir Serbinenko Reviewed-by: Daniel Kiper --- grub-core/fs/xfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index cc0becbbe..1bc4017ca 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -971,7 +971,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, * Leaf and tail information are only in the data block if the number * of extents is 1. */ - if (dir->inode.nextents == grub_cpu_to_be32_compile_time (1)) + if (grub_xfs_get_inode_nextents (&dir->inode) == 1) { struct grub_xfs_dirblock_tail *tail = grub_xfs_dir_tail (dir->data, dirblock); @@ -1028,7 +1028,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, * The expected number of directory entries is only tracked for the * single extent case. */ - if (dir->inode.nextents == grub_cpu_to_be32_compile_time (1)) + if (grub_xfs_get_inode_nextents (&dir->inode) == 1) { /* Check if last direntry in this block is reached. */ entries--; From c448f511e74cb7c776b314fcb7943f98d3f22b6d Mon Sep 17 00:00:00 2001 From: Maxim Suhanov Date: Fri, 28 Feb 2025 17:00:53 +0300 Subject: [PATCH 395/402] kern/rescue_reader: Block the rescue mode until the CLI authentication This further mitigates potential misuse of the CLI after the root device has been successfully unlocked via TPM. Fixes: CVE-2025-4382 Signed-off-by: Maxim Suhanov Reviewed-by: Daniel Kiper --- grub-core/kern/rescue_reader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/kern/rescue_reader.c b/grub-core/kern/rescue_reader.c index 4259857ba..a71ada8fb 100644 --- a/grub-core/kern/rescue_reader.c +++ b/grub-core/kern/rescue_reader.c @@ -79,7 +79,7 @@ void __attribute__ ((noreturn)) grub_rescue_run (void) { /* Stall if the CLI has been disabled */ - if (grub_is_cli_disabled ()) + if (grub_is_cli_disabled () || grub_is_cli_need_auth ()) { grub_printf ("Rescue mode has been disabled...\n"); From ed691c0e0e20d9d0e8d8305a120e8c61d6be3d38 Mon Sep 17 00:00:00 2001 From: Maxim Suhanov Date: Sat, 1 Mar 2025 14:16:48 +0300 Subject: [PATCH 396/402] commands/search: Introduce the --cryptodisk-only argument This allows users to restrict the "search" command's scope to encrypted disks only. Typically, this command is used to "rebase" $root and $prefix before loading additional configuration files via "source" or "configfile". Unfortunately, this leads to security problems, like CVE-2023-4001, when an unexpected, attacker-controlled device is chosen by the "search" command. The --cryptodisk-only argument allows users to ensure that the file system picked is encrypted. This feature supports the CLI authentication, blocking bypass attempts. Signed-off-by: Maxim Suhanov Reviewed-by: Daniel Kiper --- grub-core/commands/search.c | 20 ++++++++++++++++++++ grub-core/commands/search_wrap.c | 7 ++++++- grub-core/normal/main.c | 3 ++- include/grub/search.h | 7 ++++--- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/grub-core/commands/search.c b/grub-core/commands/search.c index 263f1501c..f6bfef958 100644 --- a/grub-core/commands/search.c +++ b/grub-core/commands/search.c @@ -86,6 +86,26 @@ iterate_device (const char *name, void *data) grub_device_close (dev); } + /* Limit to encrypted disks when requested. */ + if (ctx->flags & SEARCH_FLAGS_CRYPTODISK_ONLY) + { + grub_device_t dev; + + dev = grub_device_open (name); + if (dev == NULL) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + if (dev->disk == NULL || dev->disk->dev->id != GRUB_DISK_DEVICE_CRYPTODISK_ID) + { + grub_device_close (dev); + grub_errno = GRUB_ERR_NONE; + return 0; + } + grub_device_close (dev); + } + #ifdef DO_SEARCH_FS_UUID #define compare_fn grub_strcasecmp #else diff --git a/grub-core/commands/search_wrap.c b/grub-core/commands/search_wrap.c index 318581f3b..5f536006c 100644 --- a/grub-core/commands/search_wrap.c +++ b/grub-core/commands/search_wrap.c @@ -41,6 +41,7 @@ static const struct grub_arg_option options[] = ARG_TYPE_STRING}, {"no-floppy", 'n', 0, N_("Do not probe any floppy drive."), 0, 0}, {"efidisk-only", 0, 0, N_("Only probe EFI disks."), 0, 0}, + {"cryptodisk-only", 0, 0, N_("Only probe encrypted disks."), 0, 0}, {"hint", 'h', GRUB_ARG_OPTION_REPEATABLE, N_("First try the device HINT. If HINT ends in comma, " "also try subpartitions"), N_("HINT"), ARG_TYPE_STRING}, @@ -75,6 +76,7 @@ enum options SEARCH_SET, SEARCH_NO_FLOPPY, SEARCH_EFIDISK_ONLY, + SEARCH_CRYPTODISK_ONLY, SEARCH_HINT, SEARCH_HINT_IEEE1275, SEARCH_HINT_BIOS, @@ -189,6 +191,9 @@ grub_cmd_search (grub_extcmd_context_t ctxt, int argc, char **args) if (state[SEARCH_EFIDISK_ONLY].set) flags |= SEARCH_FLAGS_EFIDISK_ONLY; + if (state[SEARCH_CRYPTODISK_ONLY].set) + flags |= SEARCH_FLAGS_CRYPTODISK_ONLY; + if (state[SEARCH_LABEL].set) grub_search_label (id, var, flags, hints, nhints); else if (state[SEARCH_FS_UUID].set) @@ -210,7 +215,7 @@ GRUB_MOD_INIT(search) cmd = grub_register_extcmd ("search", grub_cmd_search, GRUB_COMMAND_FLAG_EXTRACTOR | GRUB_COMMAND_ACCEPT_DASH, - N_("[-f|-l|-u|-s|-n] [--hint HINT [--hint HINT] ...]" + N_("[-f|-l|-u|-s|-n] [--cryptodisk-only] [--hint HINT [--hint HINT] ...]" " NAME"), N_("Search devices by file, filesystem label" " or filesystem UUID." diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index 04d058f55..96abfda2f 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -511,7 +511,8 @@ static const char *features[] = { "feature_chainloader_bpb", "feature_ntldr", "feature_platform_search_hint", "feature_default_font_path", "feature_all_video_module", "feature_menuentry_id", "feature_menuentry_options", "feature_200_final", - "feature_nativedisk_cmd", "feature_timeout_style" + "feature_nativedisk_cmd", "feature_timeout_style", + "feature_search_cryptodisk_only" }; GRUB_MOD_INIT(normal) diff --git a/include/grub/search.h b/include/grub/search.h index ffd2411ca..3eabaf0cc 100644 --- a/include/grub/search.h +++ b/include/grub/search.h @@ -21,9 +21,10 @@ enum search_flags { - SEARCH_FLAGS_NONE = 0, - SEARCH_FLAGS_NO_FLOPPY = 1, - SEARCH_FLAGS_EFIDISK_ONLY = 2 + SEARCH_FLAGS_NONE = 0, + SEARCH_FLAGS_NO_FLOPPY = 1, + SEARCH_FLAGS_EFIDISK_ONLY = 2, + SEARCH_FLAGS_CRYPTODISK_ONLY = 4 }; void grub_search_fs_file (const char *key, const char *var, From 7a584fbde0c339816a57d55fc165a854039cf0b2 Mon Sep 17 00:00:00 2001 From: Maxim Suhanov Date: Sun, 2 Mar 2025 18:08:22 +0300 Subject: [PATCH 397/402] disk/diskfilter: Introduce the "cryptocheck" command This command examines a given diskfilter device, e.g., an LVM disk, and checks if underlying disks, physical volumes, are cryptodisks, e.g., LUKS disks, this layout is called "LVM-on-LUKS". The return value is 0 when all underlying disks (of a given device) are cryptodisks (1 if at least one disk is unencrypted or in an unknown state). Users are encouraged to include the relevant check before loading anything from an LVM disk that is supposed to be encrypted. This further supports the CLI authentication, blocking bypass attempts when booting from an encrypted LVM disk. Signed-off-by: Maxim Suhanov Reviewed-by: Daniel Kiper --- grub-core/disk/diskfilter.c | 75 +++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/grub-core/disk/diskfilter.c b/grub-core/disk/diskfilter.c index cc5062691..9eda22e3f 100644 --- a/grub-core/disk/diskfilter.c +++ b/grub-core/disk/diskfilter.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -1377,6 +1378,73 @@ grub_diskfilter_get_pv_from_disk (grub_disk_t disk, } #endif +static int +grub_diskfilter_check_pvs_encrypted (grub_disk_t disk, int *pvs_cnt) +{ + struct grub_diskfilter_lv *lv = disk->data; + struct grub_diskfilter_pv *pv; + + *pvs_cnt = 0; + + if (lv->vg->pvs) + for (pv = lv->vg->pvs; pv; pv = pv->next) + { + (*pvs_cnt)++; + + if (pv->disk == NULL) + { + /* Can be a partially activated VG, bail out. */ + return GRUB_ERR_TEST_FAILURE; + } + + if (pv->disk->dev->id != GRUB_DISK_DEVICE_CRYPTODISK_ID) + { + /* All backing devices must be cryptodisks, stop. */ + return GRUB_ERR_TEST_FAILURE; + } + } + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_cryptocheck (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + grub_disk_t disk; + int check_pvs_res; + int namelen; + int pvs_cnt; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("disk name expected")); + + namelen = grub_strlen (args[0]); + if (namelen > 2 && (args[0][0] == '(') && (args[0][namelen - 1] == ')')) + args[0][namelen - 1] = 0; + else + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("invalid disk: %s"), + args[0]); + + if (!is_valid_diskfilter_name (&args[0][1])) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("unrecognized disk: %s"), + &args[0][1]); + + disk = grub_disk_open (&args[0][1]); + if (disk == NULL) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("no such disk: %s"), + &args[0][1]); + + check_pvs_res = grub_diskfilter_check_pvs_encrypted (disk, &pvs_cnt); + grub_disk_close (disk); + + grub_printf("%s is %sencrypted (%d pv%s examined)\n", &args[0][1], + (check_pvs_res == GRUB_ERR_NONE) ? "" : "un", + pvs_cnt, + (pvs_cnt > 1) ? "s" : ""); + + return check_pvs_res; +} + static struct grub_disk_dev grub_diskfilter_dev = { .name = "diskfilter", @@ -1393,14 +1461,21 @@ static struct grub_disk_dev grub_diskfilter_dev = .next = 0 }; +static grub_command_t cmd; + GRUB_MOD_INIT(diskfilter) { grub_disk_dev_register (&grub_diskfilter_dev); + cmd = grub_register_command ("cryptocheck", grub_cmd_cryptocheck, + N_("DEVICE"), + N_("Check if a logical volume resides on encrypted disks.")); } GRUB_MOD_FINI(diskfilter) { grub_disk_dev_unregister (&grub_diskfilter_dev); + if (cmd != NULL) + grub_unregister_command (cmd); free_array (); } From 10d778c4b4d56cc36836d86a9698bc5272b12101 Mon Sep 17 00:00:00 2001 From: Maxim Suhanov Date: Sun, 2 Mar 2025 23:32:43 +0300 Subject: [PATCH 398/402] commands/search: Add the diskfilter support When the --cryptodisk-only argument is given, also check the target device using the "cryptocheck" command, if available. This extends the checks to common layouts like LVM-on-LUKS, so the --cryptodisk-only argument transparently handles such setups. Signed-off-by: Maxim Suhanov Reviewed-by: Daniel Kiper --- grub-core/commands/search.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/grub-core/commands/search.c b/grub-core/commands/search.c index f6bfef958..185c1e70f 100644 --- a/grub-core/commands/search.c +++ b/grub-core/commands/search.c @@ -54,6 +54,36 @@ struct search_ctx int is_cache; }; +static bool +is_unencrypted_disk (grub_disk_t disk) +{ + grub_command_t cmd; + char *disk_str; + int disk_str_len; + int res; + + if (disk->dev->id == GRUB_DISK_DEVICE_CRYPTODISK_ID) + return false; /* This is (crypto) disk. */ + + if (disk->dev->id == GRUB_DISK_DEVICE_DISKFILTER_ID) + { + cmd = grub_command_find ("cryptocheck"); + if (cmd == NULL) /* No diskfilter module loaded for some reason. */ + return true; + + disk_str_len = grub_strlen (disk->name) + 2 + 1; + disk_str = grub_malloc (disk_str_len); + if (disk_str == NULL) /* Something is wrong, better report as unencrypted. */ + return true; + + grub_snprintf (disk_str, disk_str_len, "(%s)", disk->name); + res = cmd->func (cmd, 1, &disk_str); + grub_free (disk_str); + return (res != GRUB_ERR_NONE) ? true : false; /* GRUB_ERR_NONE for encrypted. */ + } + return true; +} + /* Helper for FUNC_NAME. */ static int iterate_device (const char *name, void *data) @@ -97,7 +127,7 @@ iterate_device (const char *name, void *data) grub_errno = GRUB_ERR_NONE; return 0; } - if (dev->disk == NULL || dev->disk->dev->id != GRUB_DISK_DEVICE_CRYPTODISK_ID) + if (dev->disk == NULL || is_unencrypted_disk (dev->disk) == true) { grub_device_close (dev); grub_errno = GRUB_ERR_NONE; From 23ec4535f40dc53f68d2709f8fb44af571431ca7 Mon Sep 17 00:00:00 2001 From: Maxim Suhanov Date: Mon, 10 Mar 2025 15:33:46 +0300 Subject: [PATCH 399/402] docs: Document available crypto disks checks Document the --cryptodisk-only argument. Also, document the "cryptocheck" command invoked when that argument is processed. Signed-off-by: Maxim Suhanov Reviewed-by: Daniel Kiper --- docs/grub.texi | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/grub.texi b/docs/grub.texi index 2b3d536d3..48438c2b6 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -4475,6 +4475,8 @@ This module provides library support for writing to a storage disk. @node diskfilter_module @section diskfilter This module provides library support for reading a disk RAID array. +It also provides support for the command @command{cryptocheck}. +@xref{cryptocheck} for more information. @node div_module @section div @@ -6427,6 +6429,7 @@ you forget a command, you can run the command @command{help} * configfile:: Load a configuration file * cpuid:: Check for CPU features * crc:: Compute or check CRC32 checksums +* cryptocheck:: Check if a device is encrypted * cryptomount:: Mount a crypto device * cutmem:: Remove memory regions * date:: Display or set current date and time @@ -6737,6 +6740,16 @@ Alias for @code{hashsum --hash crc32 arg @dots{}}. See command @command{hashsum} (@pxref{hashsum}) for full description. @end deffn +@node cryptocheck +@subsection cryptocheck + +@deffn Command cryptocheck device +Check if a given diskfilter device is backed by encrypted devices +(@pxref{cryptomount} for additional information). + +The command examines all backing devices, physical volumes, of a specified +logical volume, like LVM2, and fails when at least one of them is unencrypted. +@end deffn @node cryptomount @subsection cryptomount @@ -7666,7 +7679,8 @@ unbootable. @xref{Using digital signatures}, for more information. @deffn Command search @ [@option{--file}|@option{--label}|@option{--fs-uuid}] @ - [@option{--set} [var]] [@option{--no-floppy}|@option{--efidisk-only}] name + [@option{--set} [var]] [@option{--no-floppy}|@option{--efidisk-only}|@option{--cryptodisk-only}] @ + name Search devices by file (@option{-f}, @option{--file}), filesystem label (@option{-l}, @option{--label}), or filesystem UUID (@option{-u}, @option{--fs-uuid}). @@ -7681,6 +7695,14 @@ devices, which can be slow. The (@option{--efidisk-only}) option prevents searching any other devices then EFI disks. This is typically used when chainloading to local EFI partition. +The (@option{--cryptodisk-only}) option prevents searching any devices other +than encrypted disks. This is typically used when booting from an encrypted +file system to ensure that no code gets executed from an unencrypted device +having the same filesystem UUID or label. + +This option implicitly invokes the command @command{cryptocheck}, if it is +available (@pxref{cryptocheck} for additional information). + The @samp{search.file}, @samp{search.fs_label}, and @samp{search.fs_uuid} commands are aliases for @samp{search --file}, @samp{search --label}, and @samp{search --fs-uuid} respectively. From 301b4ef25a8fafaeba48498e97efd28bd2809f97 Mon Sep 17 00:00:00 2001 From: Maxim Suhanov Date: Tue, 4 Mar 2025 14:02:25 +0300 Subject: [PATCH 400/402] disk/cryptodisk: Add the "erase secrets" function This commit adds the grub_cryptodisk_erasesecrets() function to wipe master keys from all cryptodisks. This function is EFI-only. Since there is no easy way to "force unmount" a given encrypted disk, this function renders all mounted cryptodisks unusable. An attempt to read them will return garbage. This is why this function must be used in "no way back" conditions. Currently, it is used when unloading the cryptodisk module and when performing the "exit" command (it is often used to switch to the next EFI application). This function is not called when performing the "chainloader" command, because the callee may return to GRUB. For this reason, users are encouraged to use "exit" instead of "chainloader" to execute third-party boot applications. This function does not guarantee that all secrets are wiped from RAM. Console output, chunks from disk read requests and other may remain. This function does not clear the IV prefix and rekey key for geli disks. Also, this commit adds the relevant documentation improvements. Signed-off-by: Maxim Suhanov Reviewed-by: Daniel Kiper --- docs/grub.texi | 6 ++++++ grub-core/commands/minicmd.c | 11 +++++++++++ grub-core/disk/cryptodisk.c | 28 ++++++++++++++++++++++++++++ include/grub/cryptodisk.h | 1 + 4 files changed, 46 insertions(+) diff --git a/docs/grub.texi b/docs/grub.texi index 48438c2b6..cc4acb27e 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -6788,6 +6788,11 @@ namespace in addition to the cryptodisk namespace. Support for plain encryption mode (plain dm-crypt) is provided via separate @command{@pxref{plainmount}} command. + +On the EFI platform, GRUB tries to erase master keys from memory when the cryptodisk +module is unloaded or the command @command{exit} is executed. All secrets remain in +memory when the command @command{chainloader} is issued, because execution can +return to GRUB on the EFI platform. @end deffn @node cutmem @@ -9406,6 +9411,7 @@ USB support provides benefits similar to ATA (for USB disks) or AT (for USB keyboards). In addition it allows USBserial. Chainloading refers to the ability to load another bootloader through the same protocol +and on some platforms, like EFI, allow that bootloader to return to the GRUB. Hints allow faster disk discovery by already knowing in advance which is the disk in question. On some platforms hints are correct unless you move the disk between boots. diff --git a/grub-core/commands/minicmd.c b/grub-core/commands/minicmd.c index 8c5ee3e60..ff4ff021c 100644 --- a/grub-core/commands/minicmd.c +++ b/grub-core/commands/minicmd.c @@ -29,6 +29,10 @@ #include #include +#ifdef GRUB_MACHINE_EFI +#include +#endif + GRUB_MOD_LICENSE ("GPLv3+"); /* cat FILE */ @@ -187,6 +191,13 @@ grub_mini_cmd_exit (struct grub_command *cmd __attribute__ ((unused)), int argc __attribute__ ((unused)), char *argv[] __attribute__ ((unused))) { +#ifdef GRUB_MACHINE_EFI + /* + * The "exit" command is often used to launch the next boot application. + * So, erase the secrets. + */ + grub_cryptodisk_erasesecrets (); +#endif grub_exit (); /* Not reached. */ } diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index 7a785a49c..544a30d61 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -1856,6 +1856,31 @@ grub_cryptodisk_challenge_password (void) return GRUB_ERR_NONE; } + +void +grub_cryptodisk_erasesecrets (void) +{ + grub_cryptodisk_t i; + grub_uint8_t *buf; + + buf = grub_zalloc (GRUB_CRYPTODISK_MAX_KEYLEN); + if (buf == NULL) + grub_fatal ("grub_cryptodisk_erasesecrets: cannot allocate memory"); + + for (i = cryptodisk_list; i != NULL; i = i->next) + if (grub_cryptodisk_setkey (i, buf, i->keysize)) + grub_fatal ("grub_cryptodisk_erasesecrets: cannot erase secrets for %s", i->source); + else + grub_printf ("Erased crypto secrets for %s\n", i->source); + /* + * Unfortunately, there is no way to "force unmount" a given disk, it may + * have mounted "child" disks as well, e.g., an LVM volume. So, this + * function MUST be called when there is no way back, e.g., when exiting. + * Otherwise, subsequent read calls for a cryptodisk will return garbage. + */ + + grub_free (buf); +} #endif /* GRUB_MACHINE_EFI */ struct grub_procfs_entry luks_script = @@ -1880,6 +1905,9 @@ GRUB_MOD_INIT (cryptodisk) GRUB_MOD_FINI (cryptodisk) { +#ifdef GRUB_MACHINE_EFI + grub_cryptodisk_erasesecrets (); +#endif grub_disk_dev_unregister (&grub_cryptodisk_dev); cryptodisk_cleanup (); grub_unregister_extcmd (cmd); diff --git a/include/grub/cryptodisk.h b/include/grub/cryptodisk.h index 5bb15751d..81e631778 100644 --- a/include/grub/cryptodisk.h +++ b/include/grub/cryptodisk.h @@ -205,5 +205,6 @@ grub_cryptodisk_t grub_cryptodisk_get_by_source_disk (grub_disk_t disk); #ifdef GRUB_MACHINE_EFI grub_err_t grub_cryptodisk_challenge_password (void); +void grub_cryptodisk_erasesecrets (void); #endif #endif From dbc0eb5bd1f40de9b394e3a86e84f46c39a23e40 Mon Sep 17 00:00:00 2001 From: Maxim Suhanov Date: Tue, 4 Mar 2025 15:27:59 +0300 Subject: [PATCH 401/402] disk/cryptodisk: Wipe the passphrase from memory Switching to another EFI boot application while there are secrets in RAM is dangerous, because not all firmware is wiping memory on free. To reduce the attack surface, wipe the passphrase acquired when unlocking an encrypted volume. Signed-off-by: Maxim Suhanov Reviewed-by: Daniel Kiper --- grub-core/disk/cryptodisk.c | 1 + 1 file changed, 1 insertion(+) diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index 544a30d61..7065bcdcb 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -1302,6 +1302,7 @@ grub_cryptodisk_scan_device_real (const char *name, if (askpass) { + grub_memset (cargs->key_data, 0, cargs->key_len); cargs->key_len = 0; grub_free (cargs->key_data); } From 73d1c959ea3417e9309ba8c6102d7d6dc7c94259 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Fri, 14 Mar 2025 19:03:39 +0800 Subject: [PATCH 402/402] cryptocheck: Add --quiet option The option can be used to suppress output if we only want to test the return value of the command. Also, mention this option in the documentation. Signed-off-by: Michael Chang Signed-off-by: Maxim Suhanov Reviewed-by: Daniel Kiper --- docs/grub.texi | 4 +++- grub-core/commands/search.c | 7 ++++++- grub-core/disk/diskfilter.c | 25 +++++++++++++++++++------ 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index cc4acb27e..34b3484dc 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -6743,12 +6743,14 @@ Alias for @code{hashsum --hash crc32 arg @dots{}}. See command @command{hashsum} @node cryptocheck @subsection cryptocheck -@deffn Command cryptocheck device +@deffn Command cryptocheck [ @option{--quiet} ] device Check if a given diskfilter device is backed by encrypted devices (@pxref{cryptomount} for additional information). The command examines all backing devices, physical volumes, of a specified logical volume, like LVM2, and fails when at least one of them is unencrypted. + +The option @option{--quiet} can be given to suppress the output. @end deffn @node cryptomount diff --git a/grub-core/commands/search.c b/grub-core/commands/search.c index 185c1e70f..49b679e80 100644 --- a/grub-core/commands/search.c +++ b/grub-core/commands/search.c @@ -67,6 +67,9 @@ is_unencrypted_disk (grub_disk_t disk) if (disk->dev->id == GRUB_DISK_DEVICE_DISKFILTER_ID) { + char opt[] = "--quiet"; + char *args[2]; + cmd = grub_command_find ("cryptocheck"); if (cmd == NULL) /* No diskfilter module loaded for some reason. */ return true; @@ -77,7 +80,9 @@ is_unencrypted_disk (grub_disk_t disk) return true; grub_snprintf (disk_str, disk_str_len, "(%s)", disk->name); - res = cmd->func (cmd, 1, &disk_str); + args[0] = opt; + args[1] = disk_str; + res = cmd->func (cmd, 2, args); grub_free (disk_str); return (res != GRUB_ERR_NONE) ? true : false; /* GRUB_ERR_NONE for encrypted. */ } diff --git a/grub-core/disk/diskfilter.c b/grub-core/disk/diskfilter.c index 9eda22e3f..3a26de60c 100644 --- a/grub-core/disk/diskfilter.c +++ b/grub-core/disk/diskfilter.c @@ -1414,6 +1414,19 @@ grub_cmd_cryptocheck (grub_command_t cmd __attribute__ ((unused)), int check_pvs_res; int namelen; int pvs_cnt; + int opt_quiet = 0; + + if (argc == 2) + { + if (grub_strcmp (args[0], "--quiet") == 0) + { + opt_quiet = 1; + argc--; + args++; + } + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized option: %s"), args[0]); + } if (argc != 1) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("disk name expected")); @@ -1436,11 +1449,11 @@ grub_cmd_cryptocheck (grub_command_t cmd __attribute__ ((unused)), check_pvs_res = grub_diskfilter_check_pvs_encrypted (disk, &pvs_cnt); grub_disk_close (disk); - - grub_printf("%s is %sencrypted (%d pv%s examined)\n", &args[0][1], - (check_pvs_res == GRUB_ERR_NONE) ? "" : "un", - pvs_cnt, - (pvs_cnt > 1) ? "s" : ""); + if (!opt_quiet) + grub_printf ("%s is %sencrypted (%d pv%s examined)\n", &args[0][1], + (check_pvs_res == GRUB_ERR_NONE) ? "" : "un", + pvs_cnt, + (pvs_cnt > 1) ? "s" : ""); return check_pvs_res; } @@ -1468,7 +1481,7 @@ GRUB_MOD_INIT(diskfilter) { grub_disk_dev_register (&grub_diskfilter_dev); cmd = grub_register_command ("cryptocheck", grub_cmd_cryptocheck, - N_("DEVICE"), + N_("[--quiet] DEVICE"), N_("Check if a logical volume resides on encrypted disks.")); }