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

Commit cc677088 authored by H. Peter Anvin's avatar H. Peter Anvin
Browse files

Merge tag 'efi-for-3.9-rc2' into x86/urgent



EFI changes for v3.9-rc2,

  * Make the EFI variable code more paranoid about running out of
    space in NVRAM, since this is the root cause of the recent issue
    where machines refuse to boot - from Matthew Garrett.

  * Some efivarfs patches that fix regressions introduced in v3.9-rc1.

Signed-off-by: default avatarH. Peter Anvin <hpa@zytor.com>
parents 60f583d5 feff5dc4
Loading
Loading
Loading
Loading
+96 −34
Original line number Diff line number Diff line
@@ -426,6 +426,44 @@ get_var_data(struct efivars *efivars, struct efi_variable *var)
	return status;
}

static efi_status_t
check_var_size_locked(struct efivars *efivars, u32 attributes,
			unsigned long size)
{
	u64 storage_size, remaining_size, max_size;
	efi_status_t status;
	const struct efivar_operations *fops = efivars->ops;

	if (!efivars->ops->query_variable_info)
		return EFI_UNSUPPORTED;

	status = fops->query_variable_info(attributes, &storage_size,
					   &remaining_size, &max_size);

	if (status != EFI_SUCCESS)
		return status;

	if (!storage_size || size > remaining_size || size > max_size ||
	    (remaining_size - size) < (storage_size / 2))
		return EFI_OUT_OF_RESOURCES;

	return status;
}


static efi_status_t
check_var_size(struct efivars *efivars, u32 attributes, unsigned long size)
{
	efi_status_t status;
	unsigned long flags;

	spin_lock_irqsave(&efivars->lock, flags);
	status = check_var_size_locked(efivars, attributes, size);
	spin_unlock_irqrestore(&efivars->lock, flags);

	return status;
}

static ssize_t
efivar_guid_read(struct efivar_entry *entry, char *buf)
{
@@ -547,6 +585,11 @@ efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count)
	}

	spin_lock_irq(&efivars->lock);

	status = check_var_size_locked(efivars, new_var->Attributes,
	       new_var->DataSize + utf16_strsize(new_var->VariableName, 1024));

	if (status == EFI_SUCCESS || status == EFI_UNSUPPORTED)
		status = efivars->ops->set_variable(new_var->VariableName,
						    &new_var->VendorGuid,
						    new_var->Attributes,
@@ -702,8 +745,7 @@ static ssize_t efivarfs_file_write(struct file *file,
	u32 attributes;
	struct inode *inode = file->f_mapping->host;
	unsigned long datasize = count - sizeof(attributes);
	unsigned long newdatasize;
	u64 storage_size, remaining_size, max_size;
	unsigned long newdatasize, varsize;
	ssize_t bytes = 0;

	if (count < sizeof(attributes))
@@ -722,27 +764,17 @@ static ssize_t efivarfs_file_write(struct file *file,
	 * amounts of memory. Pick a default size of 64K if
	 * QueryVariableInfo() isn't supported by the firmware.
	 */
	spin_lock_irq(&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);
	}

	spin_unlock_irq(&efivars->lock);
	varsize = datasize + utf16_strsize(var->var.VariableName, 1024);
	status = check_var_size(efivars, attributes, varsize);

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

		remaining_size = 65536;
	}

	if (datasize > remaining_size)
		if (datasize > 65536)
			return -ENOSPC;
	}

	data = kmalloc(datasize, GFP_KERNEL);
	if (!data)
@@ -765,6 +797,19 @@ static ssize_t efivarfs_file_write(struct file *file,
	 */
	spin_lock_irq(&efivars->lock);

	/*
	 * Ensure that the available space hasn't shrunk below the safe level
	 */

	status = check_var_size_locked(efivars, attributes, varsize);

	if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED) {
		spin_unlock_irq(&efivars->lock);
		kfree(data);

		return efi_status_to_err(status);
	}

	status = efivars->ops->set_variable(var->var.VariableName,
					    &var->var.VendorGuid,
					    attributes, datasize,
@@ -929,8 +974,8 @@ static bool efivarfs_valid_name(const char *str, int len)
	if (len < GUID_LEN + 2)
		return false;

	/* GUID should be right after the first '-' */
	if (s - 1 != strchr(str, '-'))
	/* GUID must be preceded by a '-' */
	if (*(s - 1) != '-')
		return false;

	/*
@@ -1118,15 +1163,22 @@ static struct dentry_operations efivarfs_d_ops = {

static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name)
{
	struct dentry *d;
	struct qstr q;
	int err;

	q.name = name;
	q.len = strlen(name);

	if (efivarfs_d_hash(NULL, NULL, &q))
		return NULL;
	err = efivarfs_d_hash(NULL, NULL, &q);
	if (err)
		return ERR_PTR(err);

	return d_alloc(parent, &q);
	d = d_alloc(parent, &q);
	if (d)
		return d;

	return ERR_PTR(-ENOMEM);
}

static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
@@ -1136,6 +1188,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
	struct efivar_entry *entry, *n;
	struct efivars *efivars = &__efivars;
	char *name;
	int err = -ENOMEM;

	efivarfs_sb = sb;

@@ -1186,8 +1239,10 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
			goto fail_name;

		dentry = efivarfs_alloc_dentry(root, name);
		if (!dentry)
		if (IS_ERR(dentry)) {
			err = PTR_ERR(dentry);
			goto fail_inode;
		}

		/* copied by the above to local storage in the dentry. */
		kfree(name);
@@ -1214,7 +1269,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
fail_name:
	kfree(name);
fail:
	return -ENOMEM;
	return err;
}

static struct dentry *efivarfs_mount(struct file_system_type *fs_type,
@@ -1345,7 +1400,6 @@ static int efi_pstore_write(enum pstore_type_id type,
	efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
	struct efivars *efivars = psi->data;
	int i, ret = 0;
	u64 storage_space, remaining_space, max_variable_size;
	efi_status_t status = EFI_NOT_FOUND;
	unsigned long flags;

@@ -1365,11 +1419,11 @@ static int efi_pstore_write(enum pstore_type_id type,
	 * size: a size of logging data
	 * DUMP_NAME_LEN * 2: a maximum size of variable name
	 */
	status = efivars->ops->query_variable_info(PSTORE_EFI_ATTRIBUTES,
						   &storage_space,
						   &remaining_space,
						   &max_variable_size);
	if (status || remaining_space < size + DUMP_NAME_LEN * 2) {

	status = check_var_size_locked(efivars, PSTORE_EFI_ATTRIBUTES,
					 size + DUMP_NAME_LEN * 2);

	if (status) {
		spin_unlock_irqrestore(&efivars->lock, flags);
		*id = part;
		return -ENOSPC;
@@ -1544,6 +1598,14 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
		return -EINVAL;
	}

	status = check_var_size_locked(efivars, new_var->Attributes,
	       new_var->DataSize + utf16_strsize(new_var->VariableName, 1024));

	if (status && status != EFI_UNSUPPORTED) {
		spin_unlock_irq(&efivars->lock);
		return efi_status_to_err(status);
	}

	/* now *really* create the variable via EFI */
	status = efivars->ops->set_variable(new_var->VariableName,
					    &new_var->VendorGuid,
+59 −0
Original line number Diff line number Diff line
@@ -125,6 +125,63 @@ test_open_unlink()
	./open-unlink $file
}

# test that we can create a range of filenames
test_valid_filenames()
{
	local attrs='\x07\x00\x00\x00'
	local ret=0

	local file_list="abc dump-type0-11-1-1362436005 1234 -"
	for f in $file_list; do
		local file=$efivarfs_mount/$f-$test_guid

		printf "$attrs\x00" > $file

		if [ ! -e $file ]; then
			echo "$file could not be created" >&2
			ret=1
		else
			rm $file
		fi
	done

	exit $ret
}

test_invalid_filenames()
{
	local attrs='\x07\x00\x00\x00'
	local ret=0

	local file_list="
		-1234-1234-1234-123456789abc
		foo
		foo-bar
		-foo-
		foo-barbazba-foob-foob-foob-foobarbazfoo
		foo-------------------------------------
		-12345678-1234-1234-1234-123456789abc
		a-12345678=1234-1234-1234-123456789abc
		a-12345678-1234=1234-1234-123456789abc
		a-12345678-1234-1234=1234-123456789abc
		a-12345678-1234-1234-1234=123456789abc
		1112345678-1234-1234-1234-123456789abc"

	for f in $file_list; do
		local file=$efivarfs_mount/$f

		printf "$attrs\x00" 2>/dev/null > $file

		if [ -e $file ]; then
			echo "Creating $file should have failed" >&2
			rm $file
			ret=1
		fi
	done

	exit $ret
}

check_prereqs

rc=0
@@ -135,5 +192,7 @@ run_test test_create_read
run_test test_delete
run_test test_zero_size_delete
run_test test_open_unlink
run_test test_valid_filenames
run_test test_invalid_filenames

exit $rc