Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 89d16665 authored by Matt Fleming's avatar Matt Fleming
Browse files

efivarfs: Use query_variable_info() to limit kmalloc()



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: default avatarJeremy Kerr <jeremy.kerr@canonical.com>
Cc: Matthew Garrett <mjg@redhat.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: default avatarMatt Fleming <matt.fleming@intel.com>
parent cfcf2f11
Loading
Loading
Loading
Loading
+34 −9
Original line number Diff line number Diff line
@@ -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;
@@ -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;
+1 −0
Original line number Diff line number Diff line
@@ -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 {