Skip to content

Commit

Permalink
efivarfs: Use query_variable_info() to limit kmalloc()
Browse files Browse the repository at this point in the history
We don't want someone who can write EFI variables to be able to
allocate arbitrarily large amounts of memory, so cap it to something
sensible like the amount of free space for EFI variables.

Acked-by: Jeremy Kerr <jeremy.kerr@canonical.com>
Cc: Matthew Garrett <mjg@redhat.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
  • Loading branch information
Matt Fleming committed Nov 13, 2012
1 parent cfcf2f1 commit 89d1666
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 9 deletions.
43 changes: 34 additions & 9 deletions drivers/firmware/efivars.c
Original file line number Diff line number Diff line change
Expand Up @@ -694,28 +694,51 @@ static ssize_t efivarfs_file_write(struct file *file,
struct inode *inode = file->f_mapping->host;
unsigned long datasize = count - sizeof(attributes);
unsigned long newdatasize;
u64 storage_size, remaining_size, max_size;
ssize_t bytes = 0;

if (count < sizeof(attributes))
return -EINVAL;

data = kmalloc(datasize, GFP_KERNEL);
if (copy_from_user(&attributes, userbuf, sizeof(attributes)))
return -EFAULT;

if (!data)
return -ENOMEM;
if (attributes & ~(EFI_VARIABLE_MASK))
return -EINVAL;

efivars = var->efivars;

if (copy_from_user(&attributes, userbuf, sizeof(attributes))) {
bytes = -EFAULT;
goto out;
/*
* Ensure that the user can't allocate arbitrarily large
* amounts of memory. Pick a default size of 64K if
* QueryVariableInfo() isn't supported by the firmware.
*/
spin_lock(&efivars->lock);

if (!efivars->ops->query_variable_info)
status = EFI_UNSUPPORTED;
else {
const struct efivar_operations *fops = efivars->ops;
status = fops->query_variable_info(attributes, &storage_size,
&remaining_size, &max_size);
}

if (attributes & ~(EFI_VARIABLE_MASK)) {
bytes = -EINVAL;
goto out;
spin_unlock(&efivars->lock);

if (status != EFI_SUCCESS) {
if (status != EFI_UNSUPPORTED)
return efi_status_to_err(status);

remaining_size = 65536;
}

if (datasize > remaining_size)
return -ENOSPC;

data = kmalloc(datasize, GFP_KERNEL);
if (!data)
return -ENOMEM;

if (copy_from_user(data, userbuf + sizeof(attributes), datasize)) {
bytes = -EFAULT;
goto out;
Expand Down Expand Up @@ -1709,6 +1732,8 @@ efivars_init(void)
ops.get_variable = efi.get_variable;
ops.set_variable = efi.set_variable;
ops.get_next_variable = efi.get_next_variable;
ops.query_variable_info = efi.query_variable_info;

error = register_efivars(&__efivars, &ops, efi_kobj);
if (error)
goto err_put;
Expand Down
1 change: 1 addition & 0 deletions include/linux/efi.h
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,7 @@ struct efivar_operations {
efi_get_variable_t *get_variable;
efi_get_next_variable_t *get_next_variable;
efi_set_variable_t *set_variable;
efi_query_variable_info_t *query_variable_info;
};

struct efivars {
Expand Down

0 comments on commit 89d1666

Please sign in to comment.