mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-28 15:20:41 +00:00
efi: efivars: Fix variable writes with unsupported query_variable_store()
commitf11a74b45d
upstream. Commit8a254d90a7
("efi: efivars: Fix variable writes without query_variable_store()") addressed an issue that was introduced during the EFI variable store refactor, where alternative implementations of the efivars layer that lacked query_variable_store() would no longer work. Unfortunately, there is another case to consider here, which was missed: if the efivars layer is backed by the EFI runtime services as usual, but the EFI implementation predates the introduction of QueryVariableInfo(), we will return EFI_UNSUPPORTED, and this is no longer being dealt with correctly. So let's fix this, and while at it, clean up the code a bit, by merging the check_var_size() routines as well as their callers. Cc: <stable@vger.kernel.org> # v6.0 Fixes:bbc6d2c6ef
("efi: vars: Switch to new wrapper layer") Signed-off-by: Ard Biesheuvel <ardb@kernel.org> Tested-by: Aditya Garg <gargaditya08@live.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
305913e213
commit
3c8f47d42e
1 changed files with 20 additions and 48 deletions
|
@ -21,29 +21,22 @@ static struct efivars *__efivars;
|
||||||
|
|
||||||
static DEFINE_SEMAPHORE(efivars_lock);
|
static DEFINE_SEMAPHORE(efivars_lock);
|
||||||
|
|
||||||
static efi_status_t check_var_size(u32 attributes, unsigned long size)
|
static efi_status_t check_var_size(bool nonblocking, u32 attributes,
|
||||||
|
unsigned long size)
|
||||||
{
|
{
|
||||||
const struct efivar_operations *fops;
|
const struct efivar_operations *fops;
|
||||||
|
efi_status_t status;
|
||||||
|
|
||||||
fops = __efivars->ops;
|
fops = __efivars->ops;
|
||||||
|
|
||||||
if (!fops->query_variable_store)
|
if (!fops->query_variable_store)
|
||||||
|
status = EFI_UNSUPPORTED;
|
||||||
|
else
|
||||||
|
status = fops->query_variable_store(attributes, size,
|
||||||
|
nonblocking);
|
||||||
|
if (status == EFI_UNSUPPORTED)
|
||||||
return (size <= SZ_64K) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
|
return (size <= SZ_64K) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
|
||||||
|
return status;
|
||||||
return fops->query_variable_store(attributes, size, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (size <= SZ_64K) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
|
|
||||||
|
|
||||||
return fops->query_variable_store(attributes, size, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -195,26 +188,6 @@ efi_status_t efivar_get_next_variable(unsigned long *name_size,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_NS_GPL(efivar_get_next_variable, EFIVAR);
|
EXPORT_SYMBOL_NS_GPL(efivar_get_next_variable, EFIVAR);
|
||||||
|
|
||||||
/*
|
|
||||||
* efivar_set_variable_blocking() - local helper function for set_variable
|
|
||||||
*
|
|
||||||
* Must be called with efivars_lock held.
|
|
||||||
*/
|
|
||||||
static efi_status_t
|
|
||||||
efivar_set_variable_blocking(efi_char16_t *name, efi_guid_t *vendor,
|
|
||||||
u32 attr, unsigned long data_size, void *data)
|
|
||||||
{
|
|
||||||
efi_status_t status;
|
|
||||||
|
|
||||||
if (data_size > 0) {
|
|
||||||
status = check_var_size(attr, data_size +
|
|
||||||
ucs2_strsize(name, 1024));
|
|
||||||
if (status != EFI_SUCCESS)
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
return __efivars->ops->set_variable(name, vendor, attr, data_size, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* efivar_set_variable_locked() - set a variable identified by name/vendor
|
* efivar_set_variable_locked() - set a variable identified by name/vendor
|
||||||
*
|
*
|
||||||
|
@ -228,23 +201,21 @@ efi_status_t efivar_set_variable_locked(efi_char16_t *name, efi_guid_t *vendor,
|
||||||
efi_set_variable_t *setvar;
|
efi_set_variable_t *setvar;
|
||||||
efi_status_t status;
|
efi_status_t status;
|
||||||
|
|
||||||
if (!nonblocking)
|
if (data_size > 0) {
|
||||||
return efivar_set_variable_blocking(name, vendor, attr,
|
status = check_var_size(nonblocking, attr,
|
||||||
data_size, data);
|
data_size + ucs2_strsize(name, 1024));
|
||||||
|
if (status != EFI_SUCCESS)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If no _nonblocking variant exists, the ordinary one
|
* If no _nonblocking variant exists, the ordinary one
|
||||||
* is assumed to be non-blocking.
|
* is assumed to be non-blocking.
|
||||||
*/
|
*/
|
||||||
setvar = __efivars->ops->set_variable_nonblocking ?:
|
setvar = __efivars->ops->set_variable_nonblocking;
|
||||||
__efivars->ops->set_variable;
|
if (!setvar || !nonblocking)
|
||||||
|
setvar = __efivars->ops->set_variable;
|
||||||
|
|
||||||
if (data_size > 0) {
|
|
||||||
status = check_var_size_nonblocking(attr, data_size +
|
|
||||||
ucs2_strsize(name, 1024));
|
|
||||||
if (status != EFI_SUCCESS)
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
return setvar(name, vendor, attr, data_size, data);
|
return setvar(name, vendor, attr, data_size, data);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_NS_GPL(efivar_set_variable_locked, EFIVAR);
|
EXPORT_SYMBOL_NS_GPL(efivar_set_variable_locked, EFIVAR);
|
||||||
|
@ -264,7 +235,8 @@ efi_status_t efivar_set_variable(efi_char16_t *name, efi_guid_t *vendor,
|
||||||
if (efivar_lock())
|
if (efivar_lock())
|
||||||
return EFI_ABORTED;
|
return EFI_ABORTED;
|
||||||
|
|
||||||
status = efivar_set_variable_blocking(name, vendor, attr, data_size, data);
|
status = efivar_set_variable_locked(name, vendor, attr, data_size,
|
||||||
|
data, false);
|
||||||
efivar_unlock();
|
efivar_unlock();
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue