Commit f5f6a60a authored by Jeremy Kerr's avatar Jeremy Kerr Committed by Matt Fleming

efivarfs: Implement exclusive access for {get,set}_variable

Currently, efivarfs does not enforce exclusion over the get_variable and
set_variable operations. Section 7.1 of UEFI requires us to only allow a
single processor to enter {get,set}_variable services at once.

This change acquires the efivars->lock over calls to these operations
from the efivarfs paths.
Signed-off-by: default avatarJeremy Kerr <jeremy.kerr@canonical.com>
Signed-off-by: default avatarMatt Fleming <matt.fleming@intel.com>
parent 5ba6e291
...@@ -690,35 +690,45 @@ static ssize_t efivarfs_file_write(struct file *file, ...@@ -690,35 +690,45 @@ static ssize_t efivarfs_file_write(struct file *file,
goto out; goto out;
} }
/*
* The lock here protects the get_variable call, the conditional
* set_variable call, and removal of the variable from the efivars
* list (in the case of an authenticated delete).
*/
spin_lock(&efivars->lock);
status = efivars->ops->set_variable(var->var.VariableName, status = efivars->ops->set_variable(var->var.VariableName,
&var->var.VendorGuid, &var->var.VendorGuid,
attributes, datasize, attributes, datasize,
data); data);
if (status != EFI_SUCCESS) {
spin_unlock(&efivars->lock);
kfree(data);
switch (status) { switch (status) {
case EFI_SUCCESS:
break;
case EFI_INVALID_PARAMETER: case EFI_INVALID_PARAMETER:
count = -EINVAL; count = -EINVAL;
goto out; break;
case EFI_OUT_OF_RESOURCES: case EFI_OUT_OF_RESOURCES:
count = -ENOSPC; count = -ENOSPC;
goto out; break;
case EFI_DEVICE_ERROR: case EFI_DEVICE_ERROR:
count = -EIO; count = -EIO;
goto out; break;
case EFI_WRITE_PROTECTED: case EFI_WRITE_PROTECTED:
count = -EROFS; count = -EROFS;
goto out; break;
case EFI_SECURITY_VIOLATION: case EFI_SECURITY_VIOLATION:
count = -EACCES; count = -EACCES;
goto out; break;
case EFI_NOT_FOUND: case EFI_NOT_FOUND:
count = -ENOENT; count = -ENOENT;
goto out; break;
default: default:
count = -EINVAL; count = -EINVAL;
goto out; }
return count;
} }
/* /*
...@@ -734,12 +744,12 @@ static ssize_t efivarfs_file_write(struct file *file, ...@@ -734,12 +744,12 @@ static ssize_t efivarfs_file_write(struct file *file,
NULL); NULL);
if (status == EFI_BUFFER_TOO_SMALL) { if (status == EFI_BUFFER_TOO_SMALL) {
spin_unlock(&efivars->lock);
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
i_size_write(inode, newdatasize + sizeof(attributes)); i_size_write(inode, newdatasize + sizeof(attributes));
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
} else if (status == EFI_NOT_FOUND) { } else if (status == EFI_NOT_FOUND) {
spin_lock(&efivars->lock);
list_del(&var->list); list_del(&var->list);
spin_unlock(&efivars->lock); spin_unlock(&efivars->lock);
efivar_unregister(var); efivar_unregister(var);
...@@ -747,6 +757,7 @@ static ssize_t efivarfs_file_write(struct file *file, ...@@ -747,6 +757,7 @@ static ssize_t efivarfs_file_write(struct file *file,
dput(file->f_dentry); dput(file->f_dentry);
} else { } else {
spin_unlock(&efivars->lock);
pr_warn("efivarfs: inconsistent EFI variable implementation? " pr_warn("efivarfs: inconsistent EFI variable implementation? "
"status = %lx\n", status); "status = %lx\n", status);
} }
...@@ -768,9 +779,11 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf, ...@@ -768,9 +779,11 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
void *data; void *data;
ssize_t size = 0; ssize_t size = 0;
spin_lock(&efivars->lock);
status = efivars->ops->get_variable(var->var.VariableName, status = efivars->ops->get_variable(var->var.VariableName,
&var->var.VendorGuid, &var->var.VendorGuid,
&attributes, &datasize, NULL); &attributes, &datasize, NULL);
spin_unlock(&efivars->lock);
if (status != EFI_BUFFER_TOO_SMALL) if (status != EFI_BUFFER_TOO_SMALL)
return 0; return 0;
...@@ -780,10 +793,13 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf, ...@@ -780,10 +793,13 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
if (!data) if (!data)
return 0; return 0;
spin_lock(&efivars->lock);
status = efivars->ops->get_variable(var->var.VariableName, status = efivars->ops->get_variable(var->var.VariableName,
&var->var.VendorGuid, &var->var.VendorGuid,
&attributes, &datasize, &attributes, &datasize,
(data + 4)); (data + 4));
spin_unlock(&efivars->lock);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
goto out_free; goto out_free;
...@@ -1004,11 +1020,13 @@ int efivarfs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1004,11 +1020,13 @@ int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
/* copied by the above to local storage in the dentry. */ /* copied by the above to local storage in the dentry. */
kfree(name); kfree(name);
spin_lock(&efivars->lock);
efivars->ops->get_variable(entry->var.VariableName, efivars->ops->get_variable(entry->var.VariableName,
&entry->var.VendorGuid, &entry->var.VendorGuid,
&entry->var.Attributes, &entry->var.Attributes,
&size, &size,
NULL); NULL);
spin_unlock(&efivars->lock);
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
inode->i_private = entry; inode->i_private = entry;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment