efi: efibc: avoid efivar API for setting variables

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>
This commit is contained in:
Ard Biesheuvel 2022-06-20 11:35:37 +02:00
Родитель 3881ee0b1e
Коммит 416581e486
2 изменённых файлов: 31 добавлений и 48 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -10,69 +10,51 @@
#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)
#define MAX_DATA_LEN 512
static int efibc_set_variable(efi_char16_t *name, efi_char16_t *value,
unsigned long len)
{
size_t i;
efi_status_t status;
for (i = 0; i < strlen(str); i++)
str16[i] = str[i];
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);
str16[i] = '\0';
}
static int efibc_set_variable(const char *name, const char *value)
{
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);
if (size > sizeof(entry->var.Data)) {
pr_err("value is too large (%zu bytes) for '%s' EFI variable\n", size, name);
return -EINVAL;
if (status != EFI_SUCCESS) {
pr_err("failed to set EFI variable: 0x%lx\n", status);
return -EIO;
}
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) {
pr_err("failed to allocate efivar entry for '%s' EFI variable\n", name);
return -ENOMEM;
}
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);