From 90bc19a94cf36a88e65e1b4774e41c9a88b2e15a Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Mar 05 2013 15:26:52 +0000 Subject: Backport 4 fixes for efivarfs (rhbz 917984) --- 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 +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 +Acked-by: Mike Waychison +Acked-by: Matt Fleming +Signed-off-by: Tony Luck +--- + 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 +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 +Cc: +Signed-off-by: Matt Fleming +--- + 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 +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-. +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 +Reported-by: Lingzhu Xiang +Cc: Josh Boyer +Cc: Jeremy Kerr +Cc: Matthew Garrett +Cc: +Signed-off-by: Matt Fleming +--- + 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 +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 +Reported-by: Lingzhu Xiang +Cc: Josh Boyer +Cc: Jeremy Kerr +Cc: Matthew Garrett +Cc: +Signed-off-by: Matt Fleming +--- + 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 +- Backport 4 fixes for efivarfs (rhbz 917984) - Enable CONFIG_IP6_NF_TARGET_MASQUERADE * Mon Mar 04 2013 Josh Boyer