Backport 4 fixes for efivarfs (rhbz 917984)
This commit is contained in:
parent
65ffa65d32
commit
90bc19a94c
|
@ -0,0 +1,736 @@
|
|||
From 27857f8a3240e35c61dedb88cbdbfbaabbd8ad2b Mon Sep 17 00:00:00 2001
|
||||
From: Seiji Aguchi <seiji.aguchi@hds.com>
|
||||
Date: Tue, 12 Feb 2013 12:59:07 -0800
|
||||
Subject: [PATCH 1/4] efivars: Disable external interrupt while holding
|
||||
efivars->lock
|
||||
|
||||
[Problem]
|
||||
There is a scenario which efi_pstore fails to log messages in a panic case.
|
||||
|
||||
- CPUA holds an efi_var->lock in either efivarfs parts
|
||||
or efi_pstore with interrupt enabled.
|
||||
- CPUB panics and sends IPI to CPUA in smp_send_stop().
|
||||
- CPUA stops with holding the lock.
|
||||
- CPUB kicks efi_pstore_write() via kmsg_dump(KSMG_DUMP_PANIC)
|
||||
but it returns without logging messages.
|
||||
|
||||
[Patch Description]
|
||||
This patch disables an external interruption while holding efivars->lock
|
||||
as follows.
|
||||
|
||||
In efi_pstore_write() and get_var_data(), spin_lock/spin_unlock is
|
||||
replaced by spin_lock_irqsave/spin_unlock_irqrestore because they may
|
||||
be called in an interrupt context.
|
||||
|
||||
In other functions, they are replaced by spin_lock_irq/spin_unlock_irq.
|
||||
because they are all called from a process context.
|
||||
|
||||
By applying this patch, we can avoid the problem above with
|
||||
a following senario.
|
||||
|
||||
- CPUA holds an efi_var->lock with interrupt disabled.
|
||||
- CPUB panics and sends IPI to CPUA in smp_send_stop().
|
||||
- CPUA receives the IPI after releasing the lock because it is
|
||||
disabling interrupt while holding the lock.
|
||||
- CPUB waits for one sec until CPUA releases the lock.
|
||||
- CPUB kicks efi_pstore_write() via kmsg_dump(KSMG_DUMP_PANIC)
|
||||
And it can hold the lock successfully.
|
||||
|
||||
Signed-off-by: Seiji Aguchi <seiji.aguchi@hds.com>
|
||||
Acked-by: Mike Waychison <mikew@google.com>
|
||||
Acked-by: Matt Fleming <matt.fleming@intel.com>
|
||||
Signed-off-by: Tony Luck <tony.luck@intel.com>
|
||||
---
|
||||
drivers/firmware/efivars.c | 84 ++++++++++++++++++++++++----------------------
|
||||
1 file changed, 43 insertions(+), 41 deletions(-)
|
||||
|
||||
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
|
||||
index bcb201c..a9277cc 100644
|
||||
--- a/drivers/firmware/efivars.c
|
||||
+++ b/drivers/firmware/efivars.c
|
||||
@@ -406,10 +406,11 @@ static efi_status_t
|
||||
get_var_data(struct efivars *efivars, struct efi_variable *var)
|
||||
{
|
||||
efi_status_t status;
|
||||
+ unsigned long flags;
|
||||
|
||||
- spin_lock(&efivars->lock);
|
||||
+ spin_lock_irqsave(&efivars->lock, flags);
|
||||
status = get_var_data_locked(efivars, var);
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irqrestore(&efivars->lock, flags);
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n",
|
||||
@@ -538,14 +539,14 @@ efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
- spin_lock(&efivars->lock);
|
||||
+ spin_lock_irq(&efivars->lock);
|
||||
status = efivars->ops->set_variable(new_var->VariableName,
|
||||
&new_var->VendorGuid,
|
||||
new_var->Attributes,
|
||||
new_var->DataSize,
|
||||
new_var->Data);
|
||||
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n",
|
||||
@@ -714,7 +715,7 @@ 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(&efivars->lock);
|
||||
+ spin_lock_irq(&efivars->lock);
|
||||
|
||||
if (!efivars->ops->query_variable_info)
|
||||
status = EFI_UNSUPPORTED;
|
||||
@@ -724,7 +725,7 @@ static ssize_t efivarfs_file_write(struct file *file,
|
||||
&remaining_size, &max_size);
|
||||
}
|
||||
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
if (status != EFI_UNSUPPORTED)
|
||||
@@ -755,7 +756,7 @@ static ssize_t efivarfs_file_write(struct file *file,
|
||||
* set_variable call, and removal of the variable from the efivars
|
||||
* list (in the case of an authenticated delete).
|
||||
*/
|
||||
- spin_lock(&efivars->lock);
|
||||
+ spin_lock_irq(&efivars->lock);
|
||||
|
||||
status = efivars->ops->set_variable(var->var.VariableName,
|
||||
&var->var.VendorGuid,
|
||||
@@ -763,7 +764,7 @@ static ssize_t efivarfs_file_write(struct file *file,
|
||||
data);
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
kfree(data);
|
||||
|
||||
return efi_status_to_err(status);
|
||||
@@ -784,21 +785,21 @@ static ssize_t efivarfs_file_write(struct file *file,
|
||||
NULL);
|
||||
|
||||
if (status == EFI_BUFFER_TOO_SMALL) {
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
mutex_lock(&inode->i_mutex);
|
||||
i_size_write(inode, newdatasize + sizeof(attributes));
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
||||
} else if (status == EFI_NOT_FOUND) {
|
||||
list_del(&var->list);
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
efivar_unregister(var);
|
||||
drop_nlink(inode);
|
||||
d_delete(file->f_dentry);
|
||||
dput(file->f_dentry);
|
||||
|
||||
} else {
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
pr_warn("efivarfs: inconsistent EFI variable implementation? "
|
||||
"status = %lx\n", status);
|
||||
}
|
||||
@@ -820,11 +821,11 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
|
||||
void *data;
|
||||
ssize_t size = 0;
|
||||
|
||||
- spin_lock(&efivars->lock);
|
||||
+ spin_lock_irq(&efivars->lock);
|
||||
status = efivars->ops->get_variable(var->var.VariableName,
|
||||
&var->var.VendorGuid,
|
||||
&attributes, &datasize, NULL);
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
|
||||
if (status != EFI_BUFFER_TOO_SMALL)
|
||||
return efi_status_to_err(status);
|
||||
@@ -834,12 +835,12 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
- spin_lock(&efivars->lock);
|
||||
+ spin_lock_irq(&efivars->lock);
|
||||
status = efivars->ops->get_variable(var->var.VariableName,
|
||||
&var->var.VendorGuid,
|
||||
&attributes, &datasize,
|
||||
(data + sizeof(attributes)));
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
size = efi_status_to_err(status);
|
||||
@@ -1005,9 +1006,9 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry,
|
||||
goto out;
|
||||
|
||||
kobject_uevent(&var->kobj, KOBJ_ADD);
|
||||
- spin_lock(&efivars->lock);
|
||||
+ spin_lock_irq(&efivars->lock);
|
||||
list_add(&var->list, &efivars->list);
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
d_instantiate(dentry, inode);
|
||||
dget(dentry);
|
||||
out:
|
||||
@@ -1024,7 +1025,7 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
struct efivars *efivars = var->efivars;
|
||||
efi_status_t status;
|
||||
|
||||
- spin_lock(&efivars->lock);
|
||||
+ spin_lock_irq(&efivars->lock);
|
||||
|
||||
status = efivars->ops->set_variable(var->var.VariableName,
|
||||
&var->var.VendorGuid,
|
||||
@@ -1032,14 +1033,14 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
|
||||
if (status == EFI_SUCCESS || status == EFI_NOT_FOUND) {
|
||||
list_del(&var->list);
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
efivar_unregister(var);
|
||||
drop_nlink(dentry->d_inode);
|
||||
dput(dentry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
return -EINVAL;
|
||||
};
|
||||
|
||||
@@ -1184,13 +1185,13 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
/* copied by the above to local storage in the dentry. */
|
||||
kfree(name);
|
||||
|
||||
- spin_lock(&efivars->lock);
|
||||
+ spin_lock_irq(&efivars->lock);
|
||||
efivars->ops->get_variable(entry->var.VariableName,
|
||||
&entry->var.VendorGuid,
|
||||
&entry->var.Attributes,
|
||||
&size,
|
||||
NULL);
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
inode->i_private = entry;
|
||||
@@ -1253,7 +1254,7 @@ static int efi_pstore_open(struct pstore_info *psi)
|
||||
{
|
||||
struct efivars *efivars = psi->data;
|
||||
|
||||
- spin_lock(&efivars->lock);
|
||||
+ spin_lock_irq(&efivars->lock);
|
||||
efivars->walk_entry = list_first_entry(&efivars->list,
|
||||
struct efivar_entry, list);
|
||||
return 0;
|
||||
@@ -1263,7 +1264,7 @@ static int efi_pstore_close(struct pstore_info *psi)
|
||||
{
|
||||
struct efivars *efivars = psi->data;
|
||||
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1339,8 +1340,9 @@ static int efi_pstore_write(enum pstore_type_id type,
|
||||
int i, ret = 0;
|
||||
u64 storage_space, remaining_space, max_variable_size;
|
||||
efi_status_t status = EFI_NOT_FOUND;
|
||||
+ unsigned long flags;
|
||||
|
||||
- spin_lock(&efivars->lock);
|
||||
+ spin_lock_irqsave(&efivars->lock, flags);
|
||||
|
||||
/*
|
||||
* Check if there is a space enough to log.
|
||||
@@ -1352,7 +1354,7 @@ static int efi_pstore_write(enum pstore_type_id type,
|
||||
&remaining_space,
|
||||
&max_variable_size);
|
||||
if (status || remaining_space < size + DUMP_NAME_LEN * 2) {
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irqrestore(&efivars->lock, flags);
|
||||
*id = part;
|
||||
return -ENOSPC;
|
||||
}
|
||||
@@ -1366,7 +1368,7 @@ static int efi_pstore_write(enum pstore_type_id type,
|
||||
efivars->ops->set_variable(efi_name, &vendor, PSTORE_EFI_ATTRIBUTES,
|
||||
size, psi->buf);
|
||||
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irqrestore(&efivars->lock, flags);
|
||||
|
||||
if (size)
|
||||
ret = efivar_create_sysfs_entry(efivars,
|
||||
@@ -1393,7 +1395,7 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
|
||||
sprintf(name, "dump-type%u-%u-%d-%lu", type, (unsigned int)id, count,
|
||||
time.tv_sec);
|
||||
|
||||
- spin_lock(&efivars->lock);
|
||||
+ spin_lock_irq(&efivars->lock);
|
||||
|
||||
for (i = 0; i < DUMP_NAME_LEN; i++)
|
||||
efi_name[i] = name[i];
|
||||
@@ -1437,7 +1439,7 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
|
||||
if (found)
|
||||
list_del(&found->list);
|
||||
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
|
||||
if (found)
|
||||
efivar_unregister(found);
|
||||
@@ -1507,7 +1509,7 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
- spin_lock(&efivars->lock);
|
||||
+ spin_lock_irq(&efivars->lock);
|
||||
|
||||
/*
|
||||
* Does this variable already exist?
|
||||
@@ -1525,7 +1527,7 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -1539,10 +1541,10 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
|
||||
if (status != EFI_SUCCESS) {
|
||||
printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n",
|
||||
status);
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
return -EIO;
|
||||
}
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
|
||||
/* Create the entry in sysfs. Locking is not required here */
|
||||
status = efivar_create_sysfs_entry(efivars,
|
||||
@@ -1570,7 +1572,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EACCES;
|
||||
|
||||
- spin_lock(&efivars->lock);
|
||||
+ spin_lock_irq(&efivars->lock);
|
||||
|
||||
/*
|
||||
* Does this variable already exist?
|
||||
@@ -1588,7 +1590,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* force the Attributes/DataSize to 0 to ensure deletion */
|
||||
@@ -1604,12 +1606,12 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
|
||||
if (status != EFI_SUCCESS) {
|
||||
printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n",
|
||||
status);
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
return -EIO;
|
||||
}
|
||||
list_del(&search_efivar->list);
|
||||
/* We need to release this lock before unregistering. */
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
efivar_unregister(search_efivar);
|
||||
|
||||
/* It's dead Jim.... */
|
||||
@@ -1724,9 +1726,9 @@ efivar_create_sysfs_entry(struct efivars *efivars,
|
||||
kfree(short_name);
|
||||
short_name = NULL;
|
||||
|
||||
- spin_lock(&efivars->lock);
|
||||
+ spin_lock_irq(&efivars->lock);
|
||||
list_add(&new_efivar->list, &efivars->list);
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1795,9 +1797,9 @@ void unregister_efivars(struct efivars *efivars)
|
||||
struct efivar_entry *entry, *n;
|
||||
|
||||
list_for_each_entry_safe(entry, n, &efivars->list, list) {
|
||||
- spin_lock(&efivars->lock);
|
||||
+ spin_lock_irq(&efivars->lock);
|
||||
list_del(&entry->list);
|
||||
- spin_unlock(&efivars->lock);
|
||||
+ spin_unlock_irq(&efivars->lock);
|
||||
efivar_unregister(entry);
|
||||
}
|
||||
if (efivars->new_var)
|
||||
--
|
||||
1.8.1.2
|
||||
|
||||
|
||||
From 19adc04301476eaa15e035b66e92cb333223c352 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew Garrett <matthew.garrett@nebula.com>
|
||||
Date: Sat, 2 Mar 2013 19:40:17 -0500
|
||||
Subject: [PATCH 2/4] efi: be more paranoid about available space when creating
|
||||
variables
|
||||
|
||||
UEFI variables are typically stored in flash. For various reasons, avaiable
|
||||
space is typically not reclaimed immediately upon the deletion of a
|
||||
variable - instead, the system will garbage collect during initialisation
|
||||
after a reboot.
|
||||
|
||||
Some systems appear to handle this garbage collection extremely poorly,
|
||||
failing if more than 50% of the system flash is in use. This can result in
|
||||
the machine refusing to boot. The safest thing to do for the moment is to
|
||||
forbid writes if they'd end up using more than half of the storage space.
|
||||
We can make this more finegrained later if we come up with a method for
|
||||
identifying the broken machines.
|
||||
|
||||
Signed-off-by: Matthew Garrett <matthew.garrett@nebula.com>
|
||||
Cc: <stable@vger.kernel.org>
|
||||
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
|
||||
---
|
||||
drivers/firmware/efivars.c | 106 +++++++++++++++++++++++++++++++++------------
|
||||
1 file changed, 79 insertions(+), 27 deletions(-)
|
||||
|
||||
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
|
||||
index a9277cc..919862b 100644
|
||||
--- a/drivers/firmware/efivars.c
|
||||
+++ b/drivers/firmware/efivars.c
|
||||
@@ -419,6 +419,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)
|
||||
{
|
||||
@@ -540,11 +578,16 @@ efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count)
|
||||
}
|
||||
|
||||
spin_lock_irq(&efivars->lock);
|
||||
- status = efivars->ops->set_variable(new_var->VariableName,
|
||||
- &new_var->VendorGuid,
|
||||
- new_var->Attributes,
|
||||
- new_var->DataSize,
|
||||
- new_var->Data);
|
||||
+
|
||||
+ 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,
|
||||
+ new_var->DataSize,
|
||||
+ new_var->Data);
|
||||
|
||||
spin_unlock_irq(&efivars->lock);
|
||||
|
||||
@@ -695,8 +738,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))
|
||||
@@ -715,28 +757,18 @@ 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 > 65536)
|
||||
+ return -ENOSPC;
|
||||
}
|
||||
|
||||
- if (datasize > remaining_size)
|
||||
- return -ENOSPC;
|
||||
-
|
||||
data = kmalloc(datasize, GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
@@ -758,6 +790,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,
|
||||
@@ -1338,7 +1383,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;
|
||||
|
||||
@@ -1349,11 +1393,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;
|
||||
@@ -1531,6 +1575,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,
|
||||
--
|
||||
1.8.1.2
|
||||
|
||||
|
||||
From 46b6e1db3a81203deaf4615637616a0266a2e6e6 Mon Sep 17 00:00:00 2001
|
||||
From: Matt Fleming <matt.fleming@intel.com>
|
||||
Date: Tue, 5 Mar 2013 07:40:16 +0000
|
||||
Subject: [PATCH 3/4] efivars: efivarfs_valid_name() should handle pstore
|
||||
syntax
|
||||
|
||||
Stricter validation was introduced with commit da27a24383b2b
|
||||
("efivarfs: guid part of filenames are case-insensitive") and commit
|
||||
47f531e8ba3b ("efivarfs: Validate filenames much more aggressively"),
|
||||
which is necessary for the guid portion of efivarfs filenames, but we
|
||||
don't need to be so strict with the first part, the variable name. The
|
||||
UEFI specification doesn't impose any constraints on variable names
|
||||
other than they be a NULL-terminated string.
|
||||
|
||||
The above commits caused a regression that resulted in users seeing
|
||||
the following message,
|
||||
|
||||
$ sudo mount -v /sys/firmware/efi/efivars mount: Cannot allocate memory
|
||||
|
||||
whenever pstore EFI variables were present in the variable store,
|
||||
since their variable names failed to pass the following check,
|
||||
|
||||
/* GUID should be right after the first '-' */
|
||||
if (s - 1 != strchr(str, '-'))
|
||||
|
||||
as a typical pstore filename is of the form, dump-type0-10-1-<guid>.
|
||||
The fix is trivial since the guid portion of the filename is GUID_LEN
|
||||
bytes, we can use (len - GUID_LEN) to ensure the '-' character is
|
||||
where we expect it to be.
|
||||
|
||||
(The bogus ENOMEM error value will be fixed in a separate patch.)
|
||||
|
||||
Reported-by: Joseph Yasi <joe.yasi@gmail.com>
|
||||
Reported-by: Lingzhu Xiang <lxiang@redhat.com>
|
||||
Cc: Josh Boyer <jwboyer@redhat.com>
|
||||
Cc: Jeremy Kerr <jk@ozlabs.org>
|
||||
Cc: Matthew Garrett <mjg59@srcf.ucam.org>
|
||||
Cc: <stable@vger.kernel.org>
|
||||
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
|
||||
---
|
||||
drivers/firmware/efivars.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
|
||||
index 919862b..fc54ddd 100644
|
||||
--- a/drivers/firmware/efivars.c
|
||||
+++ b/drivers/firmware/efivars.c
|
||||
@@ -967,8 +967,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;
|
||||
|
||||
/*
|
||||
--
|
||||
1.8.1.2
|
||||
|
||||
|
||||
From f751b6c973fe5a480ff12c97df4b8ac4e9a666a7 Mon Sep 17 00:00:00 2001
|
||||
From: Matt Fleming <matt.fleming@intel.com>
|
||||
Date: Tue, 5 Mar 2013 12:46:30 +0000
|
||||
Subject: [PATCH 4/4] efivarfs: return accurate error code in
|
||||
efivarfs_fill_super()
|
||||
|
||||
Joseph was hitting a failure case when mounting efivarfs which
|
||||
resulted in an incorrect error message,
|
||||
|
||||
$ sudo mount -v /sys/firmware/efi/efivars mount: Cannot allocate memory
|
||||
|
||||
triggered when efivarfs_valid_name() returned -EINVAL.
|
||||
|
||||
Make sure we pass accurate return values up the stack if
|
||||
efivarfs_fill_super() fails to build inodes for EFI variables.
|
||||
|
||||
Reported-by: Joseph Yasi <joe.yasi@gmail.com>
|
||||
Reported-by: Lingzhu Xiang <lxiang@redhat.com>
|
||||
Cc: Josh Boyer <jwboyer@redhat.com>
|
||||
Cc: Jeremy Kerr <jk@ozlabs.org>
|
||||
Cc: Matthew Garrett <mjg59@srcf.ucam.org>
|
||||
Cc: <stable@vger.kernel.org>
|
||||
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
|
||||
---
|
||||
drivers/firmware/efivars.c | 20 +++++++++++++++-----
|
||||
1 file changed, 15 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
|
||||
index fc54ddd..2a2e145 100644
|
||||
--- a/drivers/firmware/efivars.c
|
||||
+++ b/drivers/firmware/efivars.c
|
||||
@@ -1156,15 +1156,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);
|
||||
+
|
||||
+ d = d_alloc(parent, &q);
|
||||
+ if (d)
|
||||
+ return d;
|
||||
|
||||
- return d_alloc(parent, &q);
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
@@ -1174,6 +1181,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;
|
||||
|
||||
@@ -1224,8 +1232,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);
|
||||
@@ -1252,7 +1262,7 @@ fail_inode:
|
||||
fail_name:
|
||||
kfree(name);
|
||||
fail:
|
||||
- return -ENOMEM;
|
||||
+ return err;
|
||||
}
|
||||
|
||||
static struct dentry *efivarfs_mount(struct file_system_type *fs_type,
|
||||
--
|
||||
1.8.1.2
|
||||
|
|
@ -62,7 +62,7 @@ Summary: The Linux kernel
|
|||
# For non-released -rc kernels, this will be appended after the rcX and
|
||||
# gitX tags, so a 3 here would become part of release "0.rcX.gitX.3"
|
||||
#
|
||||
%global baserelease 202
|
||||
%global baserelease 203
|
||||
%global fedora_build %{baserelease}
|
||||
|
||||
# base_sublevel is the kernel version we're starting with and patching
|
||||
|
@ -755,6 +755,9 @@ Patch22262: x86-mm-Fix-vmalloc_fault-oops-during-lazy-MMU-updates.patch
|
|||
#rhbz 916544
|
||||
Patch22263: 0001-drivers-crypto-nx-fix-init-race-alignmasks-and-GCM-b.patch
|
||||
|
||||
#rhbz 917984
|
||||
Patch22264: efi-fixes-3.8.patch
|
||||
|
||||
#rhbz 812111
|
||||
Patch24000: alps.patch
|
||||
|
||||
|
@ -1468,6 +1471,9 @@ ApplyPatch x86-mm-Fix-vmalloc_fault-oops-during-lazy-MMU-updates.patch
|
|||
#rhbz 916544
|
||||
ApplyPatch 0001-drivers-crypto-nx-fix-init-race-alignmasks-and-GCM-b.patch
|
||||
|
||||
#rhbz 917984
|
||||
ApplyPatch efi-fixes-3.8.patch
|
||||
|
||||
ApplyPatch userns-avoid-recursion-in-put_user_ns.patch
|
||||
|
||||
|
||||
|
@ -2329,6 +2335,7 @@ fi
|
|||
# || ||
|
||||
%changelog
|
||||
* Tue Mar 05 2013 Josh Boyer <jwboyer@redhat.com>
|
||||
- Backport 4 fixes for efivarfs (rhbz 917984)
|
||||
- Enable CONFIG_IP6_NF_TARGET_MASQUERADE
|
||||
|
||||
* Mon Mar 04 2013 Josh Boyer <jwboyer@redhat.com>
|
||||
|
|
Loading…
Reference in New Issue