Skip to content

Commit

Permalink
efi: efivars: Fix variable writes without query_variable_store()
Browse files Browse the repository at this point in the history
Commit bbc6d2c ("efi: vars: Switch to new wrapper layer")
refactored the efivars layer so that the 'business logic' related to
which UEFI variables affect the boot flow in which way could be moved
out of it, and into the efivarfs driver.

This inadvertently broke setting variables on firmware implementations
that lack the QueryVariableInfo() boot service, because we no longer
tolerate a EFI_UNSUPPORTED result from check_var_size() when calling
efivar_entry_set_get_size(), which now ends up calling check_var_size()
a second time inadvertently.

If QueryVariableInfo() is missing, we support writes of up to 64k -
let's move that logic into check_var_size(), and drop the redundant
call.

Cc: <stable@vger.kernel.org> # v6.0
Fixes: bbc6d2c ("efi: vars: Switch to new wrapper layer")
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
  • Loading branch information
Ard Biesheuvel committed Oct 21, 2022
1 parent 4b017e5 commit 8a254d9
Show file tree
Hide file tree
Showing 3 changed files with 5 additions and 24 deletions.
10 changes: 5 additions & 5 deletions drivers/firmware/efi/vars.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

#include <linux/types.h>
#include <linux/sizes.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
Expand All @@ -20,31 +21,30 @@ static struct efivars *__efivars;

static DEFINE_SEMAPHORE(efivars_lock);

efi_status_t check_var_size(u32 attributes, unsigned long size)
static efi_status_t check_var_size(u32 attributes, unsigned long size)
{
const struct efivar_operations *fops;

fops = __efivars->ops;

if (!fops->query_variable_store)
return EFI_UNSUPPORTED;
return (size <= SZ_64K) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;

return fops->query_variable_store(attributes, size, false);
}
EXPORT_SYMBOL_NS_GPL(check_var_size, EFIVAR);

static
efi_status_t check_var_size_nonblocking(u32 attributes, unsigned long size)
{
const struct efivar_operations *fops;

fops = __efivars->ops;

if (!fops->query_variable_store)
return EFI_UNSUPPORTED;
return (size <= SZ_64K) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;

return fops->query_variable_store(attributes, size, true);
}
EXPORT_SYMBOL_NS_GPL(check_var_size_nonblocking, EFIVAR);

/**
* efivars_kobject - get the kobject for the registered efivars
Expand Down
16 changes: 0 additions & 16 deletions fs/efivarfs/vars.c
Original file line number Diff line number Diff line change
Expand Up @@ -651,22 +651,6 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
if (err)
return err;

/*
* Ensure that the available space hasn't shrunk below the safe level
*/
status = check_var_size(attributes, *size + ucs2_strsize(name, 1024));
if (status != EFI_SUCCESS) {
if (status != EFI_UNSUPPORTED) {
err = efi_status_to_err(status);
goto out;
}

if (*size > 65536) {
err = -ENOSPC;
goto out;
}
}

status = efivar_set_variable_locked(name, vendor, attributes, *size,
data, false);
if (status != EFI_SUCCESS) {
Expand Down
3 changes: 0 additions & 3 deletions include/linux/efi.h
Original file line number Diff line number Diff line change
Expand Up @@ -1085,9 +1085,6 @@ efi_status_t efivar_set_variable_locked(efi_char16_t *name, efi_guid_t *vendor,
efi_status_t efivar_set_variable(efi_char16_t *name, efi_guid_t *vendor,
u32 attr, unsigned long data_size, void *data);

efi_status_t check_var_size(u32 attributes, unsigned long size);
efi_status_t check_var_size_nonblocking(u32 attributes, unsigned long size);

#if IS_ENABLED(CONFIG_EFI_CAPSULE_LOADER)
extern bool efi_capsule_pending(int *reset_type);

Expand Down

0 comments on commit 8a254d9

Please sign in to comment.