diff options
author | Luca Boccassi <bluca@debian.org> | 2022-11-28 15:49:17 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-28 15:49:17 +0100 |
commit | 6d3fcb6ccdc60fc023f27583261cecd2ff78b601 (patch) | |
tree | e7d3d4c4a85f28bc77a86d446b659d72667f2565 /src/boot | |
parent | systemctl: deprecate passing positional argument to reboot completely (diff) | |
parent | stub: Detect empty LoadOptions when run from EFI shell (diff) | |
download | systemd-6d3fcb6ccdc60fc023f27583261cecd2ff78b601.tar.xz systemd-6d3fcb6ccdc60fc023f27583261cecd2ff78b601.zip |
Merge pull request #25222 from medhefgo/stub-cmdline
stub: Fix cmdline handling
Diffstat (limited to 'src/boot')
-rw-r--r-- | src/boot/efi/boot.c | 46 | ||||
-rw-r--r-- | src/boot/efi/cpio.c | 19 | ||||
-rw-r--r-- | src/boot/efi/efi-string.c | 78 | ||||
-rw-r--r-- | src/boot/efi/efi-string.h | 5 | ||||
-rw-r--r-- | src/boot/efi/linux.c | 12 | ||||
-rw-r--r-- | src/boot/efi/linux.h | 17 | ||||
-rw-r--r-- | src/boot/efi/linux_x86.c | 21 | ||||
-rw-r--r-- | src/boot/efi/measure.c | 2 | ||||
-rw-r--r-- | src/boot/efi/missing_efi.h | 13 | ||||
-rw-r--r-- | src/boot/efi/stub.c | 85 | ||||
-rw-r--r-- | src/boot/efi/test-efi-string.c | 27 | ||||
-rw-r--r-- | src/boot/efi/util.c | 135 | ||||
-rw-r--r-- | src/boot/efi/util.h | 5 |
13 files changed, 261 insertions, 204 deletions
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 85e936b866..9123c9a84c 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -1204,7 +1204,7 @@ static void config_defaults_load_from_file(Config *config, char *content) { continue; } free(config->entry_default_config); - config->entry_default_config = xstra_to_str(value); + config->entry_default_config = xstr8_to_16(value); continue; } @@ -1418,32 +1418,32 @@ static void config_entry_add_type1( while ((line = line_get_key_value(content, " \t", &pos, &key, &value))) { if (streq8(key, "title")) { free(entry->title); - entry->title = xstra_to_str(value); + entry->title = xstr8_to_16(value); continue; } if (streq8(key, "sort-key")) { free(entry->sort_key); - entry->sort_key = xstra_to_str(value); + entry->sort_key = xstr8_to_16(value); continue; } if (streq8(key, "version")) { free(entry->version); - entry->version = xstra_to_str(value); + entry->version = xstr8_to_16(value); continue; } if (streq8(key, "machine-id")) { free(entry->machine_id); - entry->machine_id = xstra_to_str(value); + entry->machine_id = xstr8_to_16(value); continue; } if (streq8(key, "linux")) { free(entry->loader); entry->type = LOADER_LINUX; - entry->loader = xstra_to_path(value); + entry->loader = xstr8_to_path(value); entry->key = 'l'; continue; } @@ -1451,7 +1451,7 @@ static void config_entry_add_type1( if (streq8(key, "efi")) { entry->type = LOADER_EFI; free(entry->loader); - entry->loader = xstra_to_path(value); + entry->loader = xstr8_to_path(value); /* do not add an entry for ourselves */ if (strcaseeq16(entry->loader, loaded_image_path)) { @@ -1472,7 +1472,7 @@ static void config_entry_add_type1( if (streq8(key, "devicetree")) { free(entry->devicetree); - entry->devicetree = xstra_to_path(value); + entry->devicetree = xstr8_to_path(value); continue; } @@ -1481,7 +1481,7 @@ static void config_entry_add_type1( entry->initrd, n_initrd == 0 ? 0 : (n_initrd + 1) * sizeof(uint16_t *), (n_initrd + 2) * sizeof(uint16_t *)); - entry->initrd[n_initrd++] = xstra_to_path(value); + entry->initrd[n_initrd++] = xstr8_to_path(value); entry->initrd[n_initrd] = NULL; continue; } @@ -1489,7 +1489,7 @@ static void config_entry_add_type1( if (streq8(key, "options")) { _cleanup_free_ char16_t *new = NULL; - new = xstra_to_str(value); + new = xstr8_to_16(value); if (entry->options) { char16_t *s = xpool_print(L"%s %s", entry->options, new); free(entry->options); @@ -2134,49 +2134,49 @@ static void config_entry_add_unified( while ((line = line_get_key_value(content, "=", &pos, &key, &value))) { if (streq8(key, "PRETTY_NAME")) { free(os_pretty_name); - os_pretty_name = xstra_to_str(value); + os_pretty_name = xstr8_to_16(value); continue; } if (streq8(key, "IMAGE_ID")) { free(os_image_id); - os_image_id = xstra_to_str(value); + os_image_id = xstr8_to_16(value); continue; } if (streq8(key, "NAME")) { free(os_name); - os_name = xstra_to_str(value); + os_name = xstr8_to_16(value); continue; } if (streq8(key, "ID")) { free(os_id); - os_id = xstra_to_str(value); + os_id = xstr8_to_16(value); continue; } if (streq8(key, "IMAGE_VERSION")) { free(os_image_version); - os_image_version = xstra_to_str(value); + os_image_version = xstr8_to_16(value); continue; } if (streq8(key, "VERSION")) { free(os_version); - os_version = xstra_to_str(value); + os_version = xstr8_to_16(value); continue; } if (streq8(key, "VERSION_ID")) { free(os_version_id); - os_version_id = xstra_to_str(value); + os_version_id = xstr8_to_16(value); continue; } if (streq8(key, "BUILD_ID")) { free(os_build_id); - os_build_id = xstra_to_str(value); + os_build_id = xstr8_to_16(value); continue; } } @@ -2219,13 +2219,11 @@ static void config_entry_add_unified( content = mfree(content); /* read the embedded cmdline file */ - err = file_read(linux_dir, f->FileName, offs[SECTION_CMDLINE], szs[SECTION_CMDLINE], &content, NULL); + size_t cmdline_len; + err = file_read(linux_dir, f->FileName, offs[SECTION_CMDLINE], szs[SECTION_CMDLINE], &content, &cmdline_len); if (err == EFI_SUCCESS) { - /* chomp the newline */ - if (content[szs[SECTION_CMDLINE] - 1] == '\n') - content[szs[SECTION_CMDLINE] - 1] = '\0'; - - entry->options = xstra_to_str(content); + entry->options = xstrn8_to_16(content, cmdline_len); + mangle_stub_cmdline(entry->options); } } } diff --git a/src/boot/efi/cpio.c b/src/boot/efi/cpio.c index 648f9f000f..76e2cd7f4e 100644 --- a/src/boot/efi/cpio.c +++ b/src/boot/efi/cpio.c @@ -359,24 +359,7 @@ static char16_t *get_dropin_dir(const EFI_DEVICE_PATH *file_path) { if (device_path_to_str(file_path, &file_path_str) != EFI_SUCCESS) return NULL; - for (char16_t *i = file_path_str, *fixed = i;; i++) { - if (*i == '\0') { - *fixed = '\0'; - break; - } - - /* Fix device path node separator. */ - if (*i == '/') - *i = '\\'; - - /* Double '\' is not allowed in EFI file paths. */ - if (fixed != file_path_str && fixed[-1] == '\\' && *i == '\\') - continue; - - *fixed = *i; - fixed++; - } - + convert_efi_path(file_path_str); return xpool_print(u"%s.extra.d", file_path_str); } diff --git a/src/boot/efi/efi-string.c b/src/boot/efi/efi-string.c index b877c6f224..2ba15673c9 100644 --- a/src/boot/efi/efi-string.c +++ b/src/boot/efi/efi-string.c @@ -9,7 +9,8 @@ # include "util.h" #else # include <stdlib.h> -# include "macro.h" +# include "alloc-util.h" +# define xnew(t, n) ASSERT_SE_PTR(new(t, n)) # define xmalloc(n) ASSERT_SE_PTR(malloc(n)) #endif @@ -138,6 +139,81 @@ DEFINE_STRCHR(char16_t, strchr16); DEFINE_STRNDUP(char, xstrndup8, strnlen8); DEFINE_STRNDUP(char16_t, xstrndup16, strnlen16); +static unsigned utf8_to_unichar(const char *utf8, size_t n, char32_t *c) { + char32_t unichar; + unsigned len; + + assert(utf8); + assert(c); + + if (!(utf8[0] & 0x80)) { + *c = utf8[0]; + return 1; + } else if ((utf8[0] & 0xe0) == 0xc0) { + len = 2; + unichar = utf8[0] & 0x1f; + } else if ((utf8[0] & 0xf0) == 0xe0) { + len = 3; + unichar = utf8[0] & 0x0f; + } else if ((utf8[0] & 0xf8) == 0xf0) { + len = 4; + unichar = utf8[0] & 0x07; + } else if ((utf8[0] & 0xfc) == 0xf8) { + len = 5; + unichar = utf8[0] & 0x03; + } else if ((utf8[0] & 0xfe) == 0xfc) { + len = 6; + unichar = utf8[0] & 0x01; + } else { + *c = UINT32_MAX; + return 1; + } + + if (len > n) { + *c = UINT32_MAX; + return len; + } + + for (unsigned i = 1; i < len; i++) { + if ((utf8[i] & 0xc0) != 0x80) { + *c = UINT32_MAX; + return len; + } + unichar <<= 6; + unichar |= utf8[i] & 0x3f; + } + + *c = unichar; + return len; +} + +/* Convert UTF-8 to UCS-2, skipping any invalid or short byte sequences. */ +char16_t *xstrn8_to_16(const char *str8, size_t n) { + if (!str8 || n == 0) + return NULL; + + size_t i = 0; + char16_t *str16 = xnew(char16_t, n + 1); + + while (n > 0 && *str8 != '\0') { + char32_t unichar; + + size_t utf8len = utf8_to_unichar(str8, n, &unichar); + str8 += utf8len; + n = LESS_BY(n, utf8len); + + switch (unichar) { + case 0 ... 0xd7ffU: + case 0xe000U ... 0xffffU: + str16[i++] = unichar; + break; + } + } + + str16[i] = '\0'; + return str16; +} + static bool efi_fnmatch_prefix(const char16_t *p, const char16_t *h, const char16_t **ret_p, const char16_t **ret_h) { assert(p); assert(h); diff --git a/src/boot/efi/efi-string.h b/src/boot/efi/efi-string.h index d4d76a7c18..e12add0b19 100644 --- a/src/boot/efi/efi-string.h +++ b/src/boot/efi/efi-string.h @@ -99,6 +99,11 @@ static inline char16_t *xstrdup16(const char16_t *s) { return xstrndup16(s, SIZE_MAX); } +char16_t *xstrn8_to_16(const char *str8, size_t n); +static inline char16_t *xstr8_to_16(const char *str8) { + return xstrn8_to_16(str8, strlen8(str8)); +} + bool efi_fnmatch(const char16_t *pattern, const char16_t *haystack); bool parse_number8(const char *s, uint64_t *ret_u, const char **ret_tail); diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c index dd7eb48c8c..48801f9dd8 100644 --- a/src/boot/efi/linux.c +++ b/src/boot/efi/linux.c @@ -93,15 +93,16 @@ static EFI_STATUS load_image(EFI_HANDLE parent, const void *source, size_t len, EFI_STATUS linux_exec( EFI_HANDLE parent, - const char *cmdline, UINTN cmdline_len, - const void *linux_buffer, UINTN linux_length, - const void *initrd_buffer, UINTN initrd_length) { + const char16_t *cmdline, + const void *linux_buffer, + size_t linux_length, + const void *initrd_buffer, + size_t initrd_length) { uint32_t compat_address; EFI_STATUS err; assert(parent); - assert(cmdline || cmdline_len == 0); assert(linux_buffer && linux_length > 0); assert(initrd_buffer || initrd_length == 0); @@ -113,7 +114,6 @@ EFI_STATUS linux_exec( return linux_exec_efi_handover( parent, cmdline, - cmdline_len, linux_buffer, linux_length, initrd_buffer, @@ -133,7 +133,7 @@ EFI_STATUS linux_exec( return log_error_status_stall(err, u"Error getting kernel loaded image protocol: %r", err); if (cmdline) { - loaded_image->LoadOptions = xstra_to_str(cmdline); + loaded_image->LoadOptions = (void *) cmdline; loaded_image->LoadOptionsSize = strsize16(loaded_image->LoadOptions); } diff --git a/src/boot/efi/linux.h b/src/boot/efi/linux.h index 19e5f5c4a8..f0a6a37ed1 100644 --- a/src/boot/efi/linux.h +++ b/src/boot/efi/linux.h @@ -2,14 +2,19 @@ #pragma once #include <efi.h> +#include <uchar.h> EFI_STATUS linux_exec( EFI_HANDLE parent, - const char *cmdline, UINTN cmdline_len, - const void *linux_buffer, UINTN linux_length, - const void *initrd_buffer, UINTN initrd_length); + const char16_t *cmdline, + const void *linux_buffer, + size_t linux_length, + const void *initrd_buffer, + size_t initrd_length); EFI_STATUS linux_exec_efi_handover( EFI_HANDLE parent, - const char *cmdline, UINTN cmdline_len, - const void *linux_buffer, UINTN linux_length, - const void *initrd_buffer, UINTN initrd_length); + const char16_t *cmdline, + const void *linux_buffer, + size_t linux_length, + const void *initrd_buffer, + size_t initrd_length); diff --git a/src/boot/efi/linux_x86.c b/src/boot/efi/linux_x86.c index 64336ce348..6a5e431107 100644 --- a/src/boot/efi/linux_x86.c +++ b/src/boot/efi/linux_x86.c @@ -126,12 +126,13 @@ static void linux_efi_handover(EFI_HANDLE parent, uintptr_t kernel, BootParams * EFI_STATUS linux_exec_efi_handover( EFI_HANDLE parent, - const char *cmdline, UINTN cmdline_len, - const void *linux_buffer, UINTN linux_length, - const void *initrd_buffer, UINTN initrd_length) { + const char16_t *cmdline, + const void *linux_buffer, + size_t linux_length, + const void *initrd_buffer, + size_t initrd_length) { assert(parent); - assert(cmdline || cmdline_len == 0); assert(linux_buffer); assert(initrd_buffer || initrd_length == 0); @@ -185,14 +186,20 @@ EFI_STATUS linux_exec_efi_handover( _cleanup_pages_ Pages cmdline_pages = {}; if (cmdline) { + size_t len = MIN(strlen16(cmdline), image_params->hdr.cmdline_size); + cmdline_pages = xmalloc_pages( can_4g ? AllocateAnyPages : AllocateMaxAddress, EfiLoaderData, - EFI_SIZE_TO_PAGES(cmdline_len + 1), + EFI_SIZE_TO_PAGES(len + 1), CMDLINE_PTR_MAX); - memcpy(PHYSICAL_ADDRESS_TO_POINTER(cmdline_pages.addr), cmdline, cmdline_len); - ((char *) PHYSICAL_ADDRESS_TO_POINTER(cmdline_pages.addr))[cmdline_len] = 0; + /* Convert cmdline to ASCII. */ + char *cmdline8 = PHYSICAL_ADDRESS_TO_POINTER(cmdline_pages.addr); + for (size_t i = 0; i < len; i++) + cmdline8[i] = cmdline[i] <= 0x7E ? cmdline[i] : ' '; + cmdline8[len] = '\0'; + boot_params->hdr.cmd_line_ptr = (uint32_t) cmdline_pages.addr; boot_params->ext_cmd_line_ptr = cmdline_pages.addr >> 32; assert(can_4g || cmdline_pages.addr <= CMDLINE_PTR_MAX); diff --git a/src/boot/efi/measure.c b/src/boot/efi/measure.c index 9a16920787..6da07d917e 100644 --- a/src/boot/efi/measure.c +++ b/src/boot/efi/measure.c @@ -187,7 +187,7 @@ EFI_STATUS tpm_log_event_ascii(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, U _cleanup_free_ char16_t *c = NULL; if (description) - c = xstra_to_str(description); + c = xstr8_to_16(description); return tpm_log_event(pcrindex, buffer, buffer_size, c, ret_measured); } diff --git a/src/boot/efi/missing_efi.h b/src/boot/efi/missing_efi.h index f9169248ec..250c84c248 100644 --- a/src/boot/efi/missing_efi.h +++ b/src/boot/efi/missing_efi.h @@ -385,3 +385,16 @@ typedef struct _EFI_CONSOLE_CONTROL_PROTOCOL { { 0xd719b2cb, 0x3d3a, 0x4596, {0xa3, 0xbc, 0xda, 0xd0, 0xe, 0x67, 0x65, 0x6f }} #endif + +#ifndef EFI_SHELL_PARAMETERS_PROTOCOL_GUID +# define EFI_SHELL_PARAMETERS_PROTOCOL_GUID \ + { 0x752f3136, 0x4e16, 0x4fdc, { 0xa2, 0x2a, 0xe5, 0xf4, 0x68, 0x12, 0xf4, 0xca } } + +typedef struct { + CHAR16 **Argv; + UINTN Argc; + void *StdIn; + void *StdOut; + void *StdErr; +} EFI_SHELL_PARAMETERS_PROTOCOL; +#endif diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 6ece3cf733..023f8ae255 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -133,16 +133,62 @@ static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) { (void) efivar_set_uint64_le(LOADER_GUID, L"StubFeatures", stub_features, 0); } +static bool use_load_options( + EFI_HANDLE stub_image, + EFI_LOADED_IMAGE_PROTOCOL *loaded_image, + bool have_cmdline, + char16_t **ret) { + + assert(stub_image); + assert(loaded_image); + assert(ret); + + /* We only allow custom command lines if we aren't in secure boot or if no cmdline was baked into + * the stub image. */ + if (secure_boot_enabled() && have_cmdline) + return false; + + /* We also do a superficial check whether first character of passed command line + * is printable character (for compat with some Dell systems which fill in garbage?). */ + if (loaded_image->LoadOptionsSize < sizeof(char16_t) || ((char16_t *) loaded_image->LoadOptions)[0] <= 0x1F) + return false; + + /* The UEFI shell registers EFI_SHELL_PARAMETERS_PROTOCOL onto images it runs. This lets us know that + * LoadOptions starts with the stub binary path which we want to strip off. */ + EFI_SHELL_PARAMETERS_PROTOCOL *shell; + if (BS->HandleProtocol(stub_image, &(EFI_GUID) EFI_SHELL_PARAMETERS_PROTOCOL_GUID, (void **) &shell) + != EFI_SUCCESS) { + /* Not running from EFI shell, use entire LoadOptions. Note that LoadOptions is a void*, so + * it could be anything! */ + *ret = xstrndup16(loaded_image->LoadOptions, loaded_image->LoadOptionsSize / sizeof(char16_t)); + mangle_stub_cmdline(*ret); + return true; + } + + if (shell->Argc < 2) + /* No arguments were provided? Then we fall back to built-in cmdline. */ + return false; + + /* Assemble the command line ourselves without our stub path. */ + *ret = xstrdup16(shell->Argv[1]); + for (size_t i = 2; i < shell->Argc; i++) { + _cleanup_free_ char16_t *old = *ret; + *ret = xpool_print(u"%s %s", old, shell->Argv[i]); + } + + mangle_stub_cmdline(*ret); + return true; +} + EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { _cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL; - UINTN credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0; - UINTN cmdline_len = 0, linux_size, initrd_size, dt_size; + size_t credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0; + size_t linux_size, initrd_size, dt_size; EFI_PHYSICAL_ADDRESS linux_base, initrd_base, dt_base; _cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {}; EFI_LOADED_IMAGE_PROTOCOL *loaded_image; - UINTN addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {}; - char *cmdline = NULL; - _cleanup_free_ char *cmdline_owned = NULL; + size_t addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {}; + _cleanup_free_ char16_t *cmdline = NULL; int sections_measured = -1, parameters_measured = -1; bool sysext_measured = false, m; uint64_t loader_features = 0; @@ -221,32 +267,19 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { /* Show splash screen as early as possible */ graphics_splash((const uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_SPLASH], szs[UNIFIED_SECTION_SPLASH]); - if (szs[UNIFIED_SECTION_CMDLINE] > 0) { - cmdline = (char *) loaded_image->ImageBase + addrs[UNIFIED_SECTION_CMDLINE]; - cmdline_len = szs[UNIFIED_SECTION_CMDLINE]; - } - - /* if we are not in secure boot mode, or none was provided, accept a custom command line and replace - * the built-in one. We also do a superficial check whether first character of passed command line - * is printable character (for compat with some Dell systems which fill in garbage?). */ - if ((!secure_boot_enabled() || cmdline_len == 0) && - loaded_image->LoadOptionsSize > 0 && - ((char16_t *) loaded_image->LoadOptions)[0] > 0x1F) { - cmdline_len = (loaded_image->LoadOptionsSize / sizeof(char16_t)) * sizeof(char); - cmdline = cmdline_owned = xnew(char, cmdline_len); - - for (UINTN i = 0; i < cmdline_len; i++) { - char16_t c = ((char16_t *) loaded_image->LoadOptions)[i]; - cmdline[i] = c > 0x1F && c < 0x7F ? c : ' '; /* convert non-printable and non_ASCII characters to spaces. */ - } - + if (use_load_options(image, loaded_image, szs[UNIFIED_SECTION_CMDLINE] > 0, &cmdline)) { /* Let's measure the passed kernel command line into the TPM. Note that this possibly * duplicates what we already did in the boot menu, if that was already used. However, since * we want the boot menu to support an EFI binary, and want to this stub to be usable from * any boot menu, let's measure things anyway. */ m = false; - (void) tpm_log_load_options(loaded_image->LoadOptions, &m); + (void) tpm_log_load_options(cmdline, &m); parameters_measured = m; + } else if (szs[UNIFIED_SECTION_CMDLINE] > 0) { + cmdline = xstrn8_to_16( + (char *) loaded_image->ImageBase + addrs[UNIFIED_SECTION_CMDLINE], + szs[UNIFIED_SECTION_CMDLINE]); + mangle_stub_cmdline(cmdline); } export_variables(loaded_image); @@ -387,7 +420,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { log_error_stall(L"Error loading embedded devicetree: %r", err); } - err = linux_exec(image, cmdline, cmdline_len, + err = linux_exec(image, cmdline, PHYSICAL_ADDRESS_TO_POINTER(linux_base), linux_size, PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size); graphics_mode(false); diff --git a/src/boot/efi/test-efi-string.c b/src/boot/efi/test-efi-string.c index 2b2359fe5c..7b43e1d629 100644 --- a/src/boot/efi/test-efi-string.c +++ b/src/boot/efi/test-efi-string.c @@ -324,6 +324,33 @@ TEST(xstrdup16) { free(s); } +TEST(xstrn8_to_16) { + char16_t *s = NULL; + + assert_se(xstrn8_to_16(NULL, 1) == NULL); + assert_se(xstrn8_to_16("a", 0) == NULL); + + assert_se(s = xstrn8_to_16("", 1)); + assert_se(streq16(s, u"")); + free(s); + + assert_se(s = xstrn8_to_16("1", 1)); + assert_se(streq16(s, u"1")); + free(s); + + assert_se(s = xstr8_to_16("abcxyzABCXYZ09 .,-_#*!\"§$%&/()=?`~")); + assert_se(streq16(s, u"abcxyzABCXYZ09 .,-_#*!\"§$%&/()=?`~")); + free(s); + + assert_se(s = xstr8_to_16("ÿⱿ𝇉 😺")); + assert_se(streq16(s, u"ÿⱿ ")); + free(s); + + assert_se(s = xstrn8_to_16("¶¶", 3)); + assert_se(streq16(s, u"¶")); + free(s); +} + #define TEST_FNMATCH_ONE(pattern, haystack, expect) \ ({ \ assert_se(fnmatch(pattern, haystack, 0) == (expect ? 0 : FNM_NOMATCH)); \ diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c index 57436dbf0c..f9aeeb4833 100644 --- a/src/boot/efi/util.c +++ b/src/boot/efi/util.c @@ -244,127 +244,36 @@ void efivar_set_time_usec(const EFI_GUID *vendor, const char16_t *name, uint64_t efivar_set(vendor, name, str, 0); } -static int utf8_to_16(const char *stra, char16_t *c) { - char16_t unichar; - UINTN len; - - assert(stra); - assert(c); - - if (!(stra[0] & 0x80)) - len = 1; - else if ((stra[0] & 0xe0) == 0xc0) - len = 2; - else if ((stra[0] & 0xf0) == 0xe0) - len = 3; - else if ((stra[0] & 0xf8) == 0xf0) - len = 4; - else if ((stra[0] & 0xfc) == 0xf8) - len = 5; - else if ((stra[0] & 0xfe) == 0xfc) - len = 6; - else - return -1; - - switch (len) { - case 1: - unichar = stra[0]; - break; - case 2: - unichar = stra[0] & 0x1f; - break; - case 3: - unichar = stra[0] & 0x0f; - break; - case 4: - unichar = stra[0] & 0x07; - break; - case 5: - unichar = stra[0] & 0x03; - break; - case 6: - unichar = stra[0] & 0x01; - break; - } - - for (UINTN i = 1; i < len; i++) { - if ((stra[i] & 0xc0) != 0x80) - return -1; - unichar <<= 6; - unichar |= stra[i] & 0x3f; - } - - *c = unichar; - return len; -} - -char16_t *xstra_to_str(const char *stra) { - UINTN strlen; - UINTN len; - UINTN i; - char16_t *str; +void convert_efi_path(char16_t *path) { + assert(path); - assert(stra); + for (size_t i = 0, fixed = 0;; i++) { + /* Fix device path node separator. */ + path[fixed] = (path[i] == '/') ? '\\' : path[i]; - len = strlen8(stra); - str = xnew(char16_t, len + 1); - - strlen = 0; - i = 0; - while (i < len) { - int utf8len; - - utf8len = utf8_to_16(stra + i, str + strlen); - if (utf8len <= 0) { - /* invalid utf8 sequence, skip the garbage */ - i++; + /* Double '\' is not allowed in EFI file paths. */ + if (fixed > 0 && path[fixed - 1] == '\\' && path[fixed] == '\\') continue; - } - strlen++; - i += utf8len; + if (path[i] == '\0') + break; + + fixed++; } - str[strlen] = '\0'; - return str; } -char16_t *xstra_to_path(const char *stra) { - char16_t *str; - UINTN strlen; - UINTN len; - UINTN i; - - assert(stra); - - len = strlen8(stra); - str = xnew(char16_t, len + 2); - - str[0] = '\\'; - strlen = 1; - i = 0; - while (i < len) { - int utf8len; - - utf8len = utf8_to_16(stra + i, str + strlen); - if (utf8len <= 0) { - /* invalid utf8 sequence, skip the garbage */ - i++; - continue; - } - - if (str[strlen] == '/') - str[strlen] = '\\'; - if (str[strlen] == '\\' && str[strlen-1] == '\\') { - /* skip double slashes */ - i += utf8len; - continue; - } +char16_t *xstr8_to_path(const char *str8) { + assert(str8); + char16_t *path = xstr8_to_16(str8); + convert_efi_path(path); + return path; +} - strlen++; - i += utf8len; - } - str[strlen] = '\0'; - return str; +void mangle_stub_cmdline(char16_t *cmdline) { + for (; *cmdline != '\0'; cmdline++) + /* Convert ASCII control characters to spaces. */ + if (*cmdline <= 0x1F) + *cmdline = ' '; } EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, UINTN off, UINTN size, char **ret, UINTN *ret_size) { diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h index 4c5b6cab13..08f732f484 100644 --- a/src/boot/efi/util.h +++ b/src/boot/efi/util.h @@ -137,8 +137,9 @@ EFI_STATUS efivar_get_uint32_le(const EFI_GUID *vendor, const char16_t *name, ui EFI_STATUS efivar_get_uint64_le(const EFI_GUID *vendor, const char16_t *name, uint64_t *ret); EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const char16_t *name, bool *ret); -char16_t *xstra_to_path(const char *stra); -char16_t *xstra_to_str(const char *stra); +void convert_efi_path(char16_t *path); +char16_t *xstr8_to_path(const char *stra); +void mangle_stub_cmdline(char16_t *cmdline); EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, UINTN off, UINTN size, char **content, UINTN *content_size); |