]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
boot: initialize return parameters on zero-length EFI variable read
authorLuca Boccassi <luca.boccassi@gmail.com>
Fri, 26 Jun 2026 16:29:31 +0000 (17:29 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Fri, 3 Jul 2026 14:19:38 +0000 (15:19 +0100)
When a variable exists but is empty, the initial size-query
GetVariable() in efivar_get_raw_full() returns EFI_SUCCESS instead of
EFI_BUFFER_TOO_SMALL: the zero-length payload already "fits" the
zero-length query buffer. The helper returns success, but does not
initialize the return parameters.
Handle a couple of corner cases by checking the return size.

Follow-up for a40960748907212883f4b7de7367e6870657016e

src/boot/efi-efivars.c
src/boot/vmm.c

index c1ae4252c3c349bcc8453cd0c194e79b4f8bbb1f..6af325966f965975633554d6fdd171d57e46a8b2 100644 (file)
@@ -191,6 +191,16 @@ EFI_STATUS efivar_get_raw_full(
 
         size_t size = 0;
         err = RT->GetVariable((char16_t *) name, (EFI_GUID *) vendor, NULL, &size, NULL);
+        if (err == EFI_SUCCESS) {
+                /* The variable exists but is empty, initialize return parameters */
+                if (ret_attributes)
+                        *ret_attributes = 0;
+                if (ret_data)
+                        *ret_data = NULL;
+                if (ret_size)
+                        *ret_size = 0;
+                return EFI_SUCCESS;
+        }
         if (err != EFI_BUFFER_TOO_SMALL)
                 return err;
 
@@ -222,6 +232,9 @@ EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const char16_t *name, b
         if (err != EFI_SUCCESS)
                 return err;
 
+        if (size == 0)
+                return EFI_BUFFER_TOO_SMALL;
+
         if (ret)
                 *ret = *b > 0;
 
index e571a3990de643831b979caf82f41f2b04768f16..09c121b9884f0a00df1f640e5ede2a096aa4d1c2 100644 (file)
@@ -62,7 +62,7 @@ bool is_direct_boot(EFI_HANDLE device) {
 EFI_STATUS vmm_open(EFI_HANDLE *ret_vmm_dev, EFI_FILE **ret_vmm_dir) {
         _cleanup_free_ EFI_HANDLE *handles = NULL;
         size_t n_handles;
-        EFI_STATUS err, dp_err;
+        EFI_STATUS err;
 
         assert(ret_vmm_dev);
         assert(ret_vmm_dir);
@@ -79,9 +79,15 @@ EFI_STATUS vmm_open(EFI_HANDLE *ret_vmm_dev, EFI_FILE **ret_vmm_dir) {
 
         for (size_t order = 0;; order++) {
                 _cleanup_free_ EFI_DEVICE_PATH *dp = NULL;
+                size_t dp_size = 0;
 
                 _cleanup_free_ char16_t *order_str = xasprintf("VMMBootOrder%04zx", order);
-                dp_err = efivar_get_raw(MAKE_GUID_PTR(VMM_BOOT_ORDER), order_str, (void**) &dp, NULL);
+                err = efivar_get_raw(MAKE_GUID_PTR(VMM_BOOT_ORDER), order_str, (void**) &dp, &dp_size);
+
+                /* Drop the device path from the (untrusted) EFI variable if it doesn't validate, so the
+                 * check below simply has to test whether it is set. */
+                if (err == EFI_SUCCESS && !device_path_is_valid(dp, dp_size))
+                        dp = mfree(dp);
 
                 for (size_t i = 0; i < n_handles; i++) {
                         _cleanup_file_close_ EFI_FILE *root_dir = NULL, *efi_dir = NULL;
@@ -92,8 +98,8 @@ EFI_STATUS vmm_open(EFI_HANDLE *ret_vmm_dev, EFI_FILE **ret_vmm_dir) {
                         if (err != EFI_SUCCESS)
                                 return err;
 
-                        /* check against VMMBootOrderNNNN (if set) */
-                        if (dp_err == EFI_SUCCESS && !device_path_startswith(fs, dp))
+                        /* check against VMMBootOrderNNNN (if set and valid) */
+                        if (dp && !device_path_startswith(fs, dp))
                                 continue;
 
                         err = open_volume(handles[i], &root_dir);
@@ -112,7 +118,7 @@ EFI_STATUS vmm_open(EFI_HANDLE *ret_vmm_dev, EFI_FILE **ret_vmm_dir) {
                         return EFI_SUCCESS;
                 }
 
-                if (dp_err != EFI_SUCCESS)
+                if (!dp)
                         return EFI_NOT_FOUND;
         }
         assert_not_reached();