efi: efibc: avoid efivar API for setting variables
authorArd Biesheuvel <ardb@kernel.org>
Mon, 20 Jun 2022 09:35:37 +0000 (11:35 +0200)
committerArd Biesheuvel <ardb@kernel.org>
Mon, 20 Jun 2022 10:43:25 +0000 (12:43 +0200)
Avoid abusing the efivar API by passing locally instantiated
efivar_entry structs into efivar_set_entry_safe(), rather than using the
API as intended. Instead, just call efi.set_variable() directly.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
drivers/firmware/efi/Kconfig
drivers/firmware/efi/efibc.c

index 7aa4717cdcac46f91dd202f868c463388eb02735..6fd4414c4836833f861d44b0595acff832dda1b7 100644 (file)
@@ -145,6 +145,7 @@ config EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER
 
 config EFI_BOOTLOADER_CONTROL
        tristate "EFI Bootloader Control"
+       select UCS2_STRING
        default n
        help
          This module installs a reboot hook, such that if reboot() is
index 15a47539dc5638206fb326789868a92a3bd381ff..8ced7af8e56d28d0dffe40e84506e6a88dd60b54 100644 (file)
 #include <linux/module.h>
 #include <linux/reboot.h>
 #include <linux/slab.h>
+#include <linux/ucs2_string.h>
 
-static void efibc_str_to_str16(const char *str, efi_char16_t *str16)
-{
-       size_t i;
-
-       for (i = 0; i < strlen(str); i++)
-               str16[i] = str[i];
-
-       str16[i] = '\0';
-}
+#define MAX_DATA_LEN   512
 
-static int efibc_set_variable(const char *name, const char *value)
+static int efibc_set_variable(efi_char16_t *name, efi_char16_t *value,
+                             unsigned long len)
 {
-       int ret;
-       efi_guid_t guid = LINUX_EFI_LOADER_ENTRY_GUID;
-       struct efivar_entry *entry;
-       size_t size = (strlen(value) + 1) * sizeof(efi_char16_t);
+       efi_status_t status;
 
-       if (size > sizeof(entry->var.Data)) {
-               pr_err("value is too large (%zu bytes) for '%s' EFI variable\n", size, name);
-               return -EINVAL;
-       }
+       status = efi.set_variable(name, &LINUX_EFI_LOADER_ENTRY_GUID,
+                                 EFI_VARIABLE_NON_VOLATILE
+                                 | EFI_VARIABLE_BOOTSERVICE_ACCESS
+                                 | EFI_VARIABLE_RUNTIME_ACCESS,
+                                 len * sizeof(efi_char16_t), value);
 
-       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
-       if (!entry) {
-               pr_err("failed to allocate efivar entry for '%s' EFI variable\n", name);
-               return -ENOMEM;
+       if (status != EFI_SUCCESS) {
+               pr_err("failed to set EFI variable: 0x%lx\n", status);
+               return -EIO;
        }
-
-       efibc_str_to_str16(name, entry->var.VariableName);
-       efibc_str_to_str16(value, (efi_char16_t *)entry->var.Data);
-       memcpy(&entry->var.VendorGuid, &guid, sizeof(guid));
-
-       ret = efivar_entry_set_safe(entry->var.VariableName,
-                                   entry->var.VendorGuid,
-                                   EFI_VARIABLE_NON_VOLATILE
-                                   | EFI_VARIABLE_BOOTSERVICE_ACCESS
-                                   | EFI_VARIABLE_RUNTIME_ACCESS,
-                                   false, size, entry->var.Data);
-
-       if (ret)
-               pr_err("failed to set %s EFI variable: 0x%x\n",
-                      name, ret);
-
-       kfree(entry);
-       return ret;
+       return 0;
 }
 
 static int efibc_reboot_notifier_call(struct notifier_block *notifier,
                                      unsigned long event, void *data)
 {
-       const char *reason = "shutdown";
+       efi_char16_t *reason = event == SYS_RESTART ? L"reboot"
+                                                   : L"shutdown";
+       const u8 *str = data;
+       efi_char16_t *wdata;
+       unsigned long l;
        int ret;
 
-       if (event == SYS_RESTART)
-               reason = "reboot";
-
-       ret = efibc_set_variable("LoaderEntryRebootReason", reason);
+       ret = efibc_set_variable(L"LoaderEntryRebootReason", reason,
+                                ucs2_strlen(reason));
        if (ret || !data)
                return NOTIFY_DONE;
 
-       efibc_set_variable("LoaderEntryOneShot", (char *)data);
+       wdata = kmalloc(MAX_DATA_LEN * sizeof(efi_char16_t), GFP_KERNEL);
+       for (l = 0; l < MAX_DATA_LEN - 1 && str[l] != '\0'; l++)
+               wdata[l] = str[l];
+       wdata[l] = L'\0';
+
+       efibc_set_variable(L"LoaderEntryOneShot", wdata, l);
 
+       kfree(wdata);
        return NOTIFY_DONE;
 }
 
@@ -84,7 +66,7 @@ static int __init efibc_init(void)
 {
        int ret;
 
-       if (!efivars_kobject() || !efivar_supports_writes())
+       if (!efi_rt_services_supported(EFI_RT_SUPPORTED_SET_VARIABLE))
                return -ENODEV;
 
        ret = register_reboot_notifier(&efibc_reboot_notifier);