ryantimwilson / rpms / systemd

Forked from rpms/systemd a month ago
Clone
2aacef
From 9489991adc3313efff58837010e53db80aebdd1b Mon Sep 17 00:00:00 2001
2aacef
From: Jan Janssen <medhefgo@web.de>
2aacef
Date: Tue, 22 Nov 2022 17:42:38 +0100
2aacef
Subject: [PATCH]  stub: Fix cmdline handling
2aacef
2aacef
This fixes some bugs that could lead to garbage getting appended to the
2aacef
command line passed to the kernel:
2aacef
 1. The .cmdline section is not guaranteed to be NUL-terminated, but it
2aacef
    was used as if it was.
2aacef
 2. The conversion of the command line to ASCII that was passed to the
2aacef
    stub ate the NUL at the end.
2aacef
 3. LoadOptions is not guaranteed to be a NUL-terminated EFI string (it
2aacef
    really should be and generally always is, though).
2aacef
2aacef
This also fixes the inconsistent mangling of the command line. If the
2aacef
.cmdline section was used ASCII controls chars (new lines in particular)
2aacef
would not be converted to spaces.
2aacef
2aacef
As part of this commit, we optimize conversion for the generic code
2aacef
instead of the (deprecated) EFI handover protocol. Previously we would
2aacef
convert to ASCII/UTF-8 and then back to EFI string for the (now) default
2aacef
generic code path. Instead we now convert to EFI string and mangle that
2aacef
back to ASCII in the EFI handover protocol path.
2aacef
2aacef
(cherry picked from commit 927ebebe588970fa2dd082a0daaef246229f009b)
2aacef
2aacef
Related: #2138081
2aacef
---
2aacef
 src/boot/efi/boot.c      | 10 ++++------
2aacef
 src/boot/efi/linux.c     | 12 ++++++------
2aacef
 src/boot/efi/linux.h     | 17 +++++++++++------
2aacef
 src/boot/efi/linux_x86.c | 21 ++++++++++++++-------
2aacef
 src/boot/efi/stub.c      | 38 +++++++++++++++++---------------------
2aacef
 src/boot/efi/util.c      |  7 +++++++
2aacef
 src/boot/efi/util.h      |  1 +
2aacef
 7 files changed, 60 insertions(+), 46 deletions(-)
2aacef
2aacef
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
2aacef
index 581043df01..426bdc3cc2 100644
2aacef
--- a/src/boot/efi/boot.c
2aacef
+++ b/src/boot/efi/boot.c
2aacef
@@ -2242,13 +2242,11 @@ static void config_entry_add_unified(
2aacef
                 content = mfree(content);
2aacef
 
2aacef
                 /* read the embedded cmdline file */
2aacef
-                err = file_read(linux_dir, f->FileName, offs[SECTION_CMDLINE], szs[SECTION_CMDLINE], &content, NULL);
2aacef
+                size_t cmdline_len;
2aacef
+                err = file_read(linux_dir, f->FileName, offs[SECTION_CMDLINE], szs[SECTION_CMDLINE], &content, &cmdline_len);
2aacef
                 if (err == EFI_SUCCESS) {
2aacef
-                        /* chomp the newline */
2aacef
-                        if (content[szs[SECTION_CMDLINE] - 1] == '\n')
2aacef
-                                content[szs[SECTION_CMDLINE] - 1] = '\0';
2aacef
-
2aacef
-                        entry->options = xstr8_to_16(content);
2aacef
+                        entry->options = xstrn8_to_16(content, cmdline_len);
2aacef
+                        mangle_stub_cmdline(entry->options);
2aacef
                 }
2aacef
         }
2aacef
 }
2aacef
diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c
2aacef
index 668510fca3..48801f9dd8 100644
2aacef
--- a/src/boot/efi/linux.c
2aacef
+++ b/src/boot/efi/linux.c
2aacef
@@ -93,15 +93,16 @@ static EFI_STATUS load_image(EFI_HANDLE parent, const void *source, size_t len,
2aacef
 
2aacef
 EFI_STATUS linux_exec(
2aacef
                 EFI_HANDLE parent,
2aacef
-                const char *cmdline, UINTN cmdline_len,
2aacef
-                const void *linux_buffer, UINTN linux_length,
2aacef
-                const void *initrd_buffer, UINTN initrd_length) {
2aacef
+                const char16_t *cmdline,
2aacef
+                const void *linux_buffer,
2aacef
+                size_t linux_length,
2aacef
+                const void *initrd_buffer,
2aacef
+                size_t initrd_length) {
2aacef
 
2aacef
         uint32_t compat_address;
2aacef
         EFI_STATUS err;
2aacef
 
2aacef
         assert(parent);
2aacef
-        assert(cmdline || cmdline_len == 0);
2aacef
         assert(linux_buffer && linux_length > 0);
2aacef
         assert(initrd_buffer || initrd_length == 0);
2aacef
 
2aacef
@@ -113,7 +114,6 @@ EFI_STATUS linux_exec(
2aacef
                 return linux_exec_efi_handover(
2aacef
                                 parent,
2aacef
                                 cmdline,
2aacef
-                                cmdline_len,
2aacef
                                 linux_buffer,
2aacef
                                 linux_length,
2aacef
                                 initrd_buffer,
2aacef
@@ -133,7 +133,7 @@ EFI_STATUS linux_exec(
2aacef
                 return log_error_status_stall(err, u"Error getting kernel loaded image protocol: %r", err);
2aacef
 
2aacef
         if (cmdline) {
2aacef
-                loaded_image->LoadOptions = xstrn8_to_16(cmdline, cmdline_len);
2aacef
+                loaded_image->LoadOptions = (void *) cmdline;
2aacef
                 loaded_image->LoadOptionsSize = strsize16(loaded_image->LoadOptions);
2aacef
         }
2aacef
 
2aacef
diff --git a/src/boot/efi/linux.h b/src/boot/efi/linux.h
2aacef
index 19e5f5c4a8..f0a6a37ed1 100644
2aacef
--- a/src/boot/efi/linux.h
2aacef
+++ b/src/boot/efi/linux.h
2aacef
@@ -2,14 +2,19 @@
2aacef
 #pragma once
2aacef
 
2aacef
 #include <efi.h>
2aacef
+#include <uchar.h>
2aacef
 
2aacef
 EFI_STATUS linux_exec(
2aacef
                 EFI_HANDLE parent,
2aacef
-                const char *cmdline, UINTN cmdline_len,
2aacef
-                const void *linux_buffer, UINTN linux_length,
2aacef
-                const void *initrd_buffer, UINTN initrd_length);
2aacef
+                const char16_t *cmdline,
2aacef
+                const void *linux_buffer,
2aacef
+                size_t linux_length,
2aacef
+                const void *initrd_buffer,
2aacef
+                size_t initrd_length);
2aacef
 EFI_STATUS linux_exec_efi_handover(
2aacef
                 EFI_HANDLE parent,
2aacef
-                const char *cmdline, UINTN cmdline_len,
2aacef
-                const void *linux_buffer, UINTN linux_length,
2aacef
-                const void *initrd_buffer, UINTN initrd_length);
2aacef
+                const char16_t *cmdline,
2aacef
+                const void *linux_buffer,
2aacef
+                size_t linux_length,
2aacef
+                const void *initrd_buffer,
2aacef
+                size_t initrd_length);
2aacef
diff --git a/src/boot/efi/linux_x86.c b/src/boot/efi/linux_x86.c
2aacef
index 64336ce348..6a5e431107 100644
2aacef
--- a/src/boot/efi/linux_x86.c
2aacef
+++ b/src/boot/efi/linux_x86.c
2aacef
@@ -126,12 +126,13 @@ static void linux_efi_handover(EFI_HANDLE parent, uintptr_t kernel, BootParams *
2aacef
 
2aacef
 EFI_STATUS linux_exec_efi_handover(
2aacef
                 EFI_HANDLE parent,
2aacef
-                const char *cmdline, UINTN cmdline_len,
2aacef
-                const void *linux_buffer, UINTN linux_length,
2aacef
-                const void *initrd_buffer, UINTN initrd_length) {
2aacef
+                const char16_t *cmdline,
2aacef
+                const void *linux_buffer,
2aacef
+                size_t linux_length,
2aacef
+                const void *initrd_buffer,
2aacef
+                size_t initrd_length) {
2aacef
 
2aacef
         assert(parent);
2aacef
-        assert(cmdline || cmdline_len == 0);
2aacef
         assert(linux_buffer);
2aacef
         assert(initrd_buffer || initrd_length == 0);
2aacef
 
2aacef
@@ -185,14 +186,20 @@ EFI_STATUS linux_exec_efi_handover(
2aacef
 
2aacef
         _cleanup_pages_ Pages cmdline_pages = {};
2aacef
         if (cmdline) {
2aacef
+                size_t len = MIN(strlen16(cmdline), image_params->hdr.cmdline_size);
2aacef
+
2aacef
                 cmdline_pages = xmalloc_pages(
2aacef
                                 can_4g ? AllocateAnyPages : AllocateMaxAddress,
2aacef
                                 EfiLoaderData,
2aacef
-                                EFI_SIZE_TO_PAGES(cmdline_len + 1),
2aacef
+                                EFI_SIZE_TO_PAGES(len + 1),
2aacef
                                 CMDLINE_PTR_MAX);
2aacef
 
2aacef
-                memcpy(PHYSICAL_ADDRESS_TO_POINTER(cmdline_pages.addr), cmdline, cmdline_len);
2aacef
-                ((char *) PHYSICAL_ADDRESS_TO_POINTER(cmdline_pages.addr))[cmdline_len] = 0;
2aacef
+                /* Convert cmdline to ASCII. */
2aacef
+                char *cmdline8 = PHYSICAL_ADDRESS_TO_POINTER(cmdline_pages.addr);
2aacef
+                for (size_t i = 0; i < len; i++)
2aacef
+                        cmdline8[i] = cmdline[i] <= 0x7E ? cmdline[i] : ' ';
2aacef
+                cmdline8[len] = '\0';
2aacef
+
2aacef
                 boot_params->hdr.cmd_line_ptr = (uint32_t) cmdline_pages.addr;
2aacef
                 boot_params->ext_cmd_line_ptr = cmdline_pages.addr >> 32;
2aacef
                 assert(can_4g || cmdline_pages.addr <= CMDLINE_PTR_MAX);
2aacef
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
2aacef
index a842c5c679..841a0e41bd 100644
2aacef
--- a/src/boot/efi/stub.c
2aacef
+++ b/src/boot/efi/stub.c
2aacef
@@ -132,14 +132,13 @@ static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
2aacef
 
2aacef
 EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
2aacef
         _cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL;
2aacef
-        UINTN credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0;
2aacef
-        UINTN cmdline_len = 0, linux_size, initrd_size, dt_size;
2aacef
+        size_t credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0;
2aacef
+        size_t linux_size, initrd_size, dt_size;
2aacef
         EFI_PHYSICAL_ADDRESS linux_base, initrd_base, dt_base;
2aacef
         _cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {};
2aacef
         EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
2aacef
-        UINTN addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {};
2aacef
-        char *cmdline = NULL;
2aacef
-        _cleanup_free_ char *cmdline_owned = NULL;
2aacef
+        size_t addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {};
2aacef
+        _cleanup_free_ char16_t *cmdline = NULL;
2aacef
         int sections_measured = -1, parameters_measured = -1;
2aacef
         bool sysext_measured = false, m;
2aacef
         EFI_STATUS err;
2aacef
@@ -208,32 +207,29 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
2aacef
         /* Show splash screen as early as possible */
2aacef
         graphics_splash((const uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_SPLASH], szs[UNIFIED_SECTION_SPLASH]);
2aacef
 
2aacef
-        if (szs[UNIFIED_SECTION_CMDLINE] > 0) {
2aacef
-                cmdline = (char *) loaded_image->ImageBase + addrs[UNIFIED_SECTION_CMDLINE];
2aacef
-                cmdline_len = szs[UNIFIED_SECTION_CMDLINE];
2aacef
-        }
2aacef
-
2aacef
         /* if we are not in secure boot mode, or none was provided, accept a custom command line and replace
2aacef
          * the built-in one. We also do a superficial check whether first character of passed command line
2aacef
          * is printable character (for compat with some Dell systems which fill in garbage?). */
2aacef
-        if ((!secure_boot_enabled() || cmdline_len == 0) &&
2aacef
-            loaded_image->LoadOptionsSize > 0 &&
2aacef
+        if ((!secure_boot_enabled() || szs[UNIFIED_SECTION_CMDLINE] == 0) &&
2aacef
+            loaded_image->LoadOptionsSize > sizeof(char16_t) &&
2aacef
             ((char16_t *) loaded_image->LoadOptions)[0] > 0x1F) {
2aacef
-                cmdline_len = (loaded_image->LoadOptionsSize / sizeof(char16_t)) * sizeof(char);
2aacef
-                cmdline = cmdline_owned = xnew(char, cmdline_len);
2aacef
-
2aacef
-                for (UINTN i = 0; i < cmdline_len; i++) {
2aacef
-                        char16_t c = ((char16_t *) loaded_image->LoadOptions)[i];
2aacef
-                        cmdline[i] = c > 0x1F && c < 0x7F ? c : ' '; /* convert non-printable and non_ASCII characters to spaces. */
2aacef
-                }
2aacef
+                /* Note that LoadOptions is a void*, so it could be anything! */
2aacef
+                cmdline = xstrndup16(
2aacef
+                                loaded_image->LoadOptions, loaded_image->LoadOptionsSize / sizeof(char16_t));
2aacef
+                mangle_stub_cmdline(cmdline);
2aacef
 
2aacef
                 /* Let's measure the passed kernel command line into the TPM. Note that this possibly
2aacef
                  * duplicates what we already did in the boot menu, if that was already used. However, since
2aacef
                  * we want the boot menu to support an EFI binary, and want to this stub to be usable from
2aacef
                  * any boot menu, let's measure things anyway. */
2aacef
                 m = false;
2aacef
-                (void) tpm_log_load_options(loaded_image->LoadOptions, &m);
2aacef
+                (void) tpm_log_load_options(cmdline, &m);
2aacef
                 parameters_measured = m;
2aacef
+        } else if (szs[UNIFIED_SECTION_CMDLINE] > 0) {
2aacef
+                cmdline = xstrn8_to_16(
2aacef
+                                (char *) loaded_image->ImageBase + addrs[UNIFIED_SECTION_CMDLINE],
2aacef
+                                szs[UNIFIED_SECTION_CMDLINE]);
2aacef
+                mangle_stub_cmdline(cmdline);
2aacef
         }
2aacef
 
2aacef
         export_variables(loaded_image);
2aacef
@@ -374,7 +370,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
2aacef
                         log_error_stall(L"Error loading embedded devicetree: %r", err);
2aacef
         }
2aacef
 
2aacef
-        err = linux_exec(image, cmdline, cmdline_len,
2aacef
+        err = linux_exec(image, cmdline,
2aacef
                          PHYSICAL_ADDRESS_TO_POINTER(linux_base), linux_size,
2aacef
                          PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size);
2aacef
         graphics_mode(false);
2aacef
diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c
2aacef
index 3268c511d0..1f07fbc38c 100644
2aacef
--- a/src/boot/efi/util.c
2aacef
+++ b/src/boot/efi/util.c
2aacef
@@ -274,6 +274,13 @@ char16_t *xstr8_to_path(const char *str8) {
2aacef
         return path;
2aacef
 }
2aacef
 
2aacef
+void mangle_stub_cmdline(char16_t *cmdline) {
2aacef
+        for (; *cmdline != '\0'; cmdline++)
2aacef
+                /* Convert ASCII control characters to spaces. */
2aacef
+                if (*cmdline <= 0x1F)
2aacef
+                        *cmdline = ' ';
2aacef
+}
2aacef
+
2aacef
 EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, UINTN off, UINTN size, char **ret, UINTN *ret_size) {
2aacef
         _cleanup_(file_closep) EFI_FILE *handle = NULL;
2aacef
         _cleanup_free_ char *buf = NULL;
2aacef
diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h
2aacef
index e4ab8138c4..f58d24fce1 100644
2aacef
--- a/src/boot/efi/util.h
2aacef
+++ b/src/boot/efi/util.h
2aacef
@@ -114,6 +114,7 @@ EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const char16_t *name, b
2aacef
 
2aacef
 void convert_efi_path(char16_t *path);
2aacef
 char16_t *xstr8_to_path(const char *stra);
2aacef
+void mangle_stub_cmdline(char16_t *cmdline);
2aacef
 
2aacef
 EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, UINTN off, UINTN size, char **content, UINTN *content_size);
2aacef