ac3a84
From c287f39f5df561968c4cb7712750e5ed23c02b29 Mon Sep 17 00:00:00 2001
ac3a84
From: Jan Janssen <medhefgo@web.de>
ac3a84
Date: Wed, 2 Nov 2022 10:25:32 +0100
ac3a84
Subject: [PATCH] stub: Detect empty LoadOptions when run from EFI shell
ac3a84
ac3a84
The EFI shell will pass the entire command line to the application it
ac3a84
starts, which includes the file path of the stub binary. This prevents
ac3a84
us from using the built-in cmdline if the command line is otherwise
ac3a84
empty.
ac3a84
ac3a84
Fortunately, the EFI shell registers a protocol on any images it starts
ac3a84
this way. The protocol even lets us access the args individually, making
ac3a84
it easy to strip the stub path off.
ac3a84
ac3a84
Fixes: #25201
ac3a84
(cherry picked from commit b17f3b3d8077ab6827549a123ac636d655fe8d4d)
ac3a84
ac3a84
Related: #2138081
ac3a84
---
ac3a84
 src/boot/efi/missing_efi.h | 13 +++++++++
ac3a84
 src/boot/efi/stub.c        | 59 +++++++++++++++++++++++++++++++-------
ac3a84
 2 files changed, 61 insertions(+), 11 deletions(-)
ac3a84
ac3a84
diff --git a/src/boot/efi/missing_efi.h b/src/boot/efi/missing_efi.h
ac3a84
index f9169248ec..250c84c248 100644
ac3a84
--- a/src/boot/efi/missing_efi.h
ac3a84
+++ b/src/boot/efi/missing_efi.h
ac3a84
@@ -385,3 +385,16 @@ typedef struct _EFI_CONSOLE_CONTROL_PROTOCOL {
ac3a84
         { 0xd719b2cb, 0x3d3a, 0x4596, {0xa3, 0xbc, 0xda, 0xd0,  0xe, 0x67, 0x65, 0x6f }}
ac3a84
 
ac3a84
 #endif
ac3a84
+
ac3a84
+#ifndef EFI_SHELL_PARAMETERS_PROTOCOL_GUID
ac3a84
+#  define EFI_SHELL_PARAMETERS_PROTOCOL_GUID \
ac3a84
+        { 0x752f3136, 0x4e16, 0x4fdc, { 0xa2, 0x2a, 0xe5, 0xf4, 0x68, 0x12, 0xf4, 0xca } }
ac3a84
+
ac3a84
+typedef struct {
ac3a84
+        CHAR16 **Argv;
ac3a84
+        UINTN Argc;
ac3a84
+        void *StdIn;
ac3a84
+        void *StdOut;
ac3a84
+        void *StdErr;
ac3a84
+} EFI_SHELL_PARAMETERS_PROTOCOL;
ac3a84
+#endif
ac3a84
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
ac3a84
index 841a0e41bd..7c42a16c70 100644
ac3a84
--- a/src/boot/efi/stub.c
ac3a84
+++ b/src/boot/efi/stub.c
ac3a84
@@ -130,6 +130,53 @@ static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
ac3a84
         (void) efivar_set_uint64_le(LOADER_GUID, L"StubFeatures", stub_features, 0);
ac3a84
 }
ac3a84
 
ac3a84
+static bool use_load_options(
ac3a84
+                EFI_HANDLE stub_image,
ac3a84
+                EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
ac3a84
+                bool have_cmdline,
ac3a84
+                char16_t **ret) {
ac3a84
+
ac3a84
+        assert(stub_image);
ac3a84
+        assert(loaded_image);
ac3a84
+        assert(ret);
ac3a84
+
ac3a84
+        /* We only allow custom command lines if we aren't in secure boot or if no cmdline was baked into
ac3a84
+         * the stub image. */
ac3a84
+        if (secure_boot_enabled() && have_cmdline)
ac3a84
+                return false;
ac3a84
+
ac3a84
+        /* We also do a superficial check whether first character of passed command line
ac3a84
+         * is printable character (for compat with some Dell systems which fill in garbage?). */
ac3a84
+        if (loaded_image->LoadOptionsSize < sizeof(char16_t) || ((char16_t *) loaded_image->LoadOptions)[0] <= 0x1F)
ac3a84
+                return false;
ac3a84
+
ac3a84
+        /* The UEFI shell registers EFI_SHELL_PARAMETERS_PROTOCOL onto images it runs. This lets us know that
ac3a84
+         * LoadOptions starts with the stub binary path which we want to strip off. */
ac3a84
+        EFI_SHELL_PARAMETERS_PROTOCOL *shell;
ac3a84
+        if (BS->HandleProtocol(stub_image, &(EFI_GUID) EFI_SHELL_PARAMETERS_PROTOCOL_GUID, (void **) &shell)
ac3a84
+            != EFI_SUCCESS) {
ac3a84
+                /* Not running from EFI shell, use entire LoadOptions. Note that LoadOptions is a void*, so
ac3a84
+                 * it could be anything! */
ac3a84
+                *ret = xstrndup16(loaded_image->LoadOptions, loaded_image->LoadOptionsSize / sizeof(char16_t));
ac3a84
+                mangle_stub_cmdline(*ret);
ac3a84
+                return true;
ac3a84
+        }
ac3a84
+
ac3a84
+        if (shell->Argc < 2)
ac3a84
+                /* No arguments were provided? Then we fall back to built-in cmdline. */
ac3a84
+                return false;
ac3a84
+
ac3a84
+        /* Assemble the command line ourselves without our stub path. */
ac3a84
+        *ret = xstrdup16(shell->Argv[1]);
ac3a84
+        for (size_t i = 2; i < shell->Argc; i++) {
ac3a84
+                _cleanup_free_ char16_t *old = *ret;
ac3a84
+                *ret = xpool_print(u"%s %s", old, shell->Argv[i]);
ac3a84
+        }
ac3a84
+
ac3a84
+        mangle_stub_cmdline(*ret);
ac3a84
+        return true;
ac3a84
+}
ac3a84
+
ac3a84
 EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
ac3a84
         _cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL;
ac3a84
         size_t credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0;
ac3a84
@@ -207,17 +254,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
ac3a84
         /* Show splash screen as early as possible */
ac3a84
         graphics_splash((const uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_SPLASH], szs[UNIFIED_SECTION_SPLASH]);
ac3a84
 
ac3a84
-        /* if we are not in secure boot mode, or none was provided, accept a custom command line and replace
ac3a84
-         * the built-in one. We also do a superficial check whether first character of passed command line
ac3a84
-         * is printable character (for compat with some Dell systems which fill in garbage?). */
ac3a84
-        if ((!secure_boot_enabled() || szs[UNIFIED_SECTION_CMDLINE] == 0) &&
ac3a84
-            loaded_image->LoadOptionsSize > sizeof(char16_t) &&
ac3a84
-            ((char16_t *) loaded_image->LoadOptions)[0] > 0x1F) {
ac3a84
-                /* Note that LoadOptions is a void*, so it could be anything! */
ac3a84
-                cmdline = xstrndup16(
ac3a84
-                                loaded_image->LoadOptions, loaded_image->LoadOptionsSize / sizeof(char16_t));
ac3a84
-                mangle_stub_cmdline(cmdline);
ac3a84
-
ac3a84
+        if (use_load_options(image, loaded_image, szs[UNIFIED_SECTION_CMDLINE] > 0, &cmdline)) {
ac3a84
                 /* Let's measure the passed kernel command line into the TPM. Note that this possibly
ac3a84
                  * duplicates what we already did in the boot menu, if that was already used. However, since
ac3a84
                  * we want the boot menu to support an EFI binary, and want to this stub to be usable from