[kernel/f18] Backport 4 fixes for efivarfs (rhbz 917984)
Josh Boyer
jwboyer at fedoraproject.org
Tue Mar 5 15:28:23 UTC 2013
commit 90bc19a94cf36a88e65e1b4774e41c9a88b2e15a
Author: Josh Boyer <jwboyer at redhat.com>
Date: Tue Mar 5 10:26:49 2013 -0500
Backport 4 fixes for efivarfs (rhbz 917984)
efi-fixes-3.8.patch | 736 +++++++++++++++++++++++++++++++++++++++++++++++++++
kernel.spec | 9 +-
2 files changed, 744 insertions(+), 1 deletions(-)
---
diff --git a/efi-fixes-3.8.patch b/efi-fixes-3.8.patch
new file mode 100644
index 0000000..f53dac0
--- /dev/null
+++ b/efi-fixes-3.8.patch
@@ -0,0 +1,736 @@
+From 27857f8a3240e35c61dedb88cbdbfbaabbd8ad2b Mon Sep 17 00:00:00 2001
+From: Seiji Aguchi <seiji.aguchi at 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 at hds.com>
+Acked-by: Mike Waychison <mikew at google.com>
+Acked-by: Matt Fleming <matt.fleming at intel.com>
+Signed-off-by: Tony Luck <tony.luck at 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 at 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 at nebula.com>
+Cc: <stable at vger.kernel.org>
+Signed-off-by: Matt Fleming <matt.fleming at 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 at 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 at gmail.com>
+Reported-by: Lingzhu Xiang <lxiang at redhat.com>
+Cc: Josh Boyer <jwboyer at redhat.com>
+Cc: Jeremy Kerr <jk at ozlabs.org>
+Cc: Matthew Garrett <mjg59 at srcf.ucam.org>
+Cc: <stable at vger.kernel.org>
+Signed-off-by: Matt Fleming <matt.fleming at 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 at 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 at gmail.com>
+Reported-by: Lingzhu Xiang <lxiang at redhat.com>
+Cc: Josh Boyer <jwboyer at redhat.com>
+Cc: Jeremy Kerr <jk at ozlabs.org>
+Cc: Matthew Garrett <mjg59 at srcf.ucam.org>
+Cc: <stable at vger.kernel.org>
+Signed-off-by: Matt Fleming <matt.fleming at 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
+
diff --git a/kernel.spec b/kernel.spec
index 2fa8498..9c6a88f 100644
--- a/kernel.spec
+++ b/kernel.spec
@@ -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 at redhat.com>
+- Backport 4 fixes for efivarfs (rhbz 917984)
- Enable CONFIG_IP6_NF_TARGET_MASQUERADE
* Mon Mar 04 2013 Josh Boyer <jwboyer at redhat.com>
More information about the scm-commits
mailing list