[kernel/f17] Backport 4 fixes for efivarfs (rhbz 917984)

Josh Boyer jwboyer at fedoraproject.org
Tue Mar 5 15:28:18 UTC 2013


commit 6ac537a02891e21c8157670c39fd055413a4bd9d
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         |   11 +-
 2 files changed, 746 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 5575560..920d058 100644
--- a/kernel.spec
+++ b/kernel.spec
@@ -54,7 +54,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 102
+%global baserelease 103
 %global fedora_build %{baserelease}
 
 # base_sublevel is the kernel version we're starting with and patching
@@ -734,6 +734,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
 
@@ -1437,6 +1440,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
 
 # END OF PATCH APPLICATIONS
@@ -2294,6 +2300,9 @@ fi
 #    '-'      |  |
 #              '-'
 %changelog
+* Tue Mar 05 2013 Josh Boyer <jwboyer at redhat.com>
+- Backport 4 fixes for efivarfs (rhbz 917984)
+
 * Mon Mar 04 2013 Josh Boyer <jwboyer at redhat.com>
 - Fix issues in nx crypto driver from Kent Yoder (rhbz 916544)
 


More information about the scm-commits mailing list