From ad37a2bcf3c6b38f5ccb54b58830a410cf2940c1 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Sat, 26 Sep 2026 18:12:52 -0700 Subject: [PATCH] fix(variables): accept UTF-16 values without a NUL terminator EFI string variables written without a trailing NUL, or with extra NUL padding, failed to decode and were silently treated as unset. Decode up to the first NUL and terminate the string ourselves, as systemd-boot does. Because empty values now decode to an empty string rather than failing, treat empty LoaderEntryDefault and LoaderEntryOneShot values as unset so an empty one-shot entry doesn't fail to match any entry. --- crates/eficore/src/bootloader_interface.rs | 9 ++++++--- crates/eficore/src/strings.rs | 8 +++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/crates/eficore/src/bootloader_interface.rs b/crates/eficore/src/bootloader_interface.rs index 87c6d01..ba6459f 100644 --- a/crates/eficore/src/bootloader_interface.rs +++ b/crates/eficore/src/bootloader_interface.rs @@ -295,9 +295,11 @@ impl BootloaderInterface { /// Get the default entry set by the bootloader interface. pub fn get_default_entry() -> Result> { - Self::VENDOR + // An empty value is treated as if the default entry was not set. + Ok(Self::VENDOR .get_cstr16("LoaderEntryDefault") - .context("unable to get default entry from bootloader interface") + .context("unable to get default entry from bootloader interface")? + .filter(|value| !value.is_empty())) } /// Get the oneshot entry set by the bootloader interface. @@ -318,6 +320,7 @@ impl BootloaderInterface { .context("unable to remove oneshot entry")?; // Return the oneshot value. - Ok(Some(value)) + // An empty value is treated as if the oneshot entry was not set. + Ok(Some(value).filter(|value| !value.is_empty())) } } diff --git a/crates/eficore/src/strings.rs b/crates/eficore/src/strings.rs index f61a21c..105c9b5 100644 --- a/crates/eficore/src/strings.rs +++ b/crates/eficore/src/strings.rs @@ -10,15 +10,21 @@ pub fn utf16_bytes_to_cstring16(bytes: &[u8]) -> Result { } // Convert the bytes to UTF-16 data. - let data = bytes + let mut data = bytes // Chunk everything into two bytes. .as_chunks::<2>() .0 .iter() // Reinterpret the bytes as u16 little-endian. .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]])) + // Stop at the first null terminator, if any. + // This ignores any trailing data, such as extra null padding. + .take_while(|c| *c != 0) // Collect the result into a vector. .collect::>(); + // Add the null terminator, as the input is not guaranteed to be null-terminated. + data.push(0); + CString16::try_from(data).context("unable to convert utf16 bytes to CString16") }