Commit 416581e4 authored by Ard Biesheuvel's avatar Ard Biesheuvel
Browse files

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: default avatarArd Biesheuvel <ardb@kernel.org>
parent 3881ee0b
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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
+29 −47
Original line number Diff line number Diff line
@@ -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)
{
	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);

	if (size > sizeof(entry->var.Data)) {
		pr_err("value is too large (%zu bytes) for '%s' EFI variable\n", size, name);
		return -EINVAL;
	}

	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));
	efi_status_t status;

	ret = efivar_entry_set_safe(entry->var.VariableName,
				    entry->var.VendorGuid,
	status = efi.set_variable(name, &LINUX_EFI_LOADER_ENTRY_GUID,
				  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);
				  len * sizeof(efi_char16_t), value);

	kfree(entry);
	return ret;
	if (status != EFI_SUCCESS) {
		pr_err("failed to set EFI variable: 0x%lx\n", status);
		return -EIO;
	}
	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);