From: Greg Kroah-Hartman Date: Tue, 2 Apr 2013 20:20:31 +0000 (-0700) Subject: 3.4-stable patches X-Git-Tag: v3.8.6~16 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c873fa11dec2168b06ea66345945bba918a08872;p=thirdparty%2Fkernel%2Fstable-queue.git 3.4-stable patches added patches: efivars-explicitly-calculate-length-of-variablename.patch efivars-handle-duplicate-names-from-get_next_variable.patch --- diff --git a/queue-3.4/efivars-explicitly-calculate-length-of-variablename.patch b/queue-3.4/efivars-explicitly-calculate-length-of-variablename.patch new file mode 100644 index 00000000000..291ff902791 --- /dev/null +++ b/queue-3.4/efivars-explicitly-calculate-length-of-variablename.patch @@ -0,0 +1,96 @@ +From ec50bd32f1672d38ddce10fb1841cbfda89cfe9a Mon Sep 17 00:00:00 2001 +From: Matt Fleming +Date: Fri, 1 Mar 2013 14:49:12 +0000 +Subject: efivars: explicitly calculate length of VariableName + +From: Matt Fleming + +commit ec50bd32f1672d38ddce10fb1841cbfda89cfe9a upstream. + +It's not wise to assume VariableNameSize represents the length of +VariableName, as not all firmware updates VariableNameSize in the same +way (some don't update it at all if EFI_SUCCESS is returned). There +are even implementations out there that update VariableNameSize with +values that are both larger than the string returned in VariableName +and smaller than the buffer passed to GetNextVariableName(), which +resulted in the following bug report from Michael Schroeder, + + > On HP z220 system (firmware version 1.54), some EFI variables are + > incorrectly named : + > + > ls -d /sys/firmware/efi/vars/*8be4d* | grep -v -- -8be returns + > /sys/firmware/efi/vars/dbxDefault-pport8be4df61-93ca-11d2-aa0d-00e098032b8c + > /sys/firmware/efi/vars/KEKDefault-pport8be4df61-93ca-11d2-aa0d-00e098032b8c + > /sys/firmware/efi/vars/SecureBoot-pport8be4df61-93ca-11d2-aa0d-00e098032b8c + > /sys/firmware/efi/vars/SetupMode-Information8be4df61-93ca-11d2-aa0d-00e098032b8c + +The issue here is that because we blindly use VariableNameSize without +verifying its value, we can potentially read garbage values from the +buffer containing VariableName if VariableNameSize is larger than the +length of VariableName. + +Since VariableName is a string, we can calculate its size by searching +for the terminating NULL character. + +[Backported for 3.8-stable. Removed workqueue code added in +a93bc0c 3.9-rc1.] + +Reported-by: Frederic Crozat +Cc: Matthew Garrett +Cc: Josh Boyer +Cc: Michael Schroeder +Cc: Lee, Chun-Yi +Cc: Lingzhu Xiang +Cc: Seiji Aguchi +Signed-off-by: Matt Fleming +Signed-off-by: Lingzhu Xiang +Reviewed-by: CAI Qian +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/firmware/efivars.c | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +--- a/drivers/firmware/efivars.c ++++ b/drivers/firmware/efivars.c +@@ -943,6 +943,31 @@ static ssize_t efivar_delete(struct file + } + + /* ++ * Returns the size of variable_name, in bytes, including the ++ * terminating NULL character, or variable_name_size if no NULL ++ * character is found among the first variable_name_size bytes. ++ */ ++static unsigned long var_name_strnsize(efi_char16_t *variable_name, ++ unsigned long variable_name_size) ++{ ++ unsigned long len; ++ efi_char16_t c; ++ ++ /* ++ * The variable name is, by definition, a NULL-terminated ++ * string, so make absolutely sure that variable_name_size is ++ * the value we expect it to be. If not, return the real size. ++ */ ++ for (len = 2; len <= variable_name_size; len += sizeof(c)) { ++ c = variable_name[(len / sizeof(c)) - 1]; ++ if (!c) ++ break; ++ } ++ ++ return min(len, variable_name_size); ++} ++ ++/* + * Let's not leave out systab information that snuck into + * the efivars driver + */ +@@ -1169,6 +1194,8 @@ int register_efivars(struct efivars *efi + &vendor_guid); + switch (status) { + case EFI_SUCCESS: ++ variable_name_size = var_name_strnsize(variable_name, ++ variable_name_size); + efivar_create_sysfs_entry(efivars, + variable_name_size, + variable_name, diff --git a/queue-3.4/efivars-handle-duplicate-names-from-get_next_variable.patch b/queue-3.4/efivars-handle-duplicate-names-from-get_next_variable.patch new file mode 100644 index 00000000000..68d0f6ce0a0 --- /dev/null +++ b/queue-3.4/efivars-handle-duplicate-names-from-get_next_variable.patch @@ -0,0 +1,192 @@ +From e971318bbed610e28bb3fde9d548e6aaf0a6b02e Mon Sep 17 00:00:00 2001 +From: Matt Fleming +Date: Thu, 7 Mar 2013 11:59:14 +0000 +Subject: efivars: Handle duplicate names from get_next_variable() + +From: Matt Fleming + +commit e971318bbed610e28bb3fde9d548e6aaf0a6b02e upstream. + +Some firmware exhibits a bug where the same VariableName and +VendorGuid values are returned on multiple invocations of +GetNextVariableName(). See, + + https://bugzilla.kernel.org/show_bug.cgi?id=47631 + +As a consequence of such a bug, Andre reports hitting the following +WARN_ON() in the sysfs code after updating the BIOS on his, "Gigabyte +Technology Co., Ltd. To be filled by O.E.M./Z77X-UD3H, BIOS F19e +11/21/2012)" machine, + +[ 0.581554] EFI Variables Facility v0.08 2004-May-17 +[ 0.584914] ------------[ cut here ]------------ +[ 0.585639] WARNING: at /home/andre/linux/fs/sysfs/dir.c:536 sysfs_add_one+0xd4/0x100() +[ 0.586381] Hardware name: To be filled by O.E.M. +[ 0.587123] sysfs: cannot create duplicate filename '/firmware/efi/vars/SbAslBufferPtrVar-01f33c25-764d-43ea-aeea-6b5a41f3f3e8' +[ 0.588694] Modules linked in: +[ 0.589484] Pid: 1, comm: swapper/0 Not tainted 3.8.0+ #7 +[ 0.590280] Call Trace: +[ 0.591066] [] ? sysfs_add_one+0xd4/0x100 +[ 0.591861] [] warn_slowpath_common+0x7f/0xc0 +[ 0.592650] [] warn_slowpath_fmt+0x4c/0x50 +[ 0.593429] [] ? strlcat+0x65/0x80 +[ 0.594203] [] sysfs_add_one+0xd4/0x100 +[ 0.594979] [] create_dir+0x78/0xd0 +[ 0.595753] [] sysfs_create_dir+0x86/0xe0 +[ 0.596532] [] kobject_add_internal+0x9c/0x220 +[ 0.597310] [] kobject_init_and_add+0x67/0x90 +[ 0.598083] [] ? efivar_create_sysfs_entry+0x61/0x1c0 +[ 0.598859] [] efivar_create_sysfs_entry+0x11b/0x1c0 +[ 0.599631] [] register_efivars+0xde/0x420 +[ 0.600395] [] ? edd_init+0x2f5/0x2f5 +[ 0.601150] [] efivars_init+0xb8/0x104 +[ 0.601903] [] do_one_initcall+0x12a/0x180 +[ 0.602659] [] kernel_init_freeable+0x13e/0x1c6 +[ 0.603418] [] ? loglevel+0x31/0x31 +[ 0.604183] [] ? rest_init+0x80/0x80 +[ 0.604936] [] kernel_init+0xe/0xf0 +[ 0.605681] [] ret_from_fork+0x7c/0xb0 +[ 0.606414] [] ? rest_init+0x80/0x80 +[ 0.607143] ---[ end trace 1609741ab737eb29 ]--- + +There's not much we can do to work around and keep traversing the +variable list once we hit this firmware bug. Our only solution is to +terminate the loop because, as Lingzhu reports, some machines get +stuck when they encounter duplicate names, + + > I had an IBM System x3100 M4 and x3850 X5 on which kernel would + > get stuck in infinite loop creating duplicate sysfs files because, + > for some reason, there are several duplicate boot entries in nvram + > getting GetNextVariableName into a circle of iteration (with + > period > 2). + +Also disable the workqueue, as efivar_update_sysfs_entries() uses +GetNextVariableName() to figure out which variables have been created +since the last iteration. That algorithm isn't going to work if +GetNextVariableName() returns duplicates. Note that we don't disable +EFI variable creation completely on the affected machines, it's just +that any pstore dump-* files won't appear in sysfs until the next +boot. + +[Backported for 3.4-stable. Removed code related to pstore +workqueue but pulled in helper function variable_is_present +from a93bc0c; Moved the definition of __efivars to the top +for being referenced in variable_is_present.] + +Reported-by: Andre Heider +Reported-by: Lingzhu Xiang +Tested-by: Lingzhu Xiang +Cc: Seiji Aguchi +Signed-off-by: Matt Fleming +Signed-off-by: Lingzhu Xiang +Reviewed-by: CAI Qian +Signed-off-by: Greg Kroah-Hartman + + +--- + drivers/firmware/efivars.c | 66 ++++++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 63 insertions(+), 3 deletions(-) + +--- a/drivers/firmware/efivars.c ++++ b/drivers/firmware/efivars.c +@@ -122,6 +122,9 @@ struct efivar_attribute { + ssize_t (*store)(struct efivar_entry *entry, const char *buf, size_t count); + }; + ++static struct efivars __efivars; ++static struct efivar_operations ops; ++ + #define PSTORE_EFI_ATTRIBUTES \ + (EFI_VARIABLE_NON_VOLATILE | \ + EFI_VARIABLE_BOOTSERVICE_ACCESS | \ +@@ -942,6 +945,28 @@ static ssize_t efivar_delete(struct file + return count; + } + ++static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor) ++{ ++ struct efivar_entry *entry, *n; ++ struct efivars *efivars = &__efivars; ++ unsigned long strsize1, strsize2; ++ bool found = false; ++ ++ strsize1 = utf16_strsize(variable_name, 1024); ++ list_for_each_entry_safe(entry, n, &efivars->list, list) { ++ strsize2 = utf16_strsize(entry->var.VariableName, 1024); ++ if (strsize1 == strsize2 && ++ !memcmp(variable_name, &(entry->var.VariableName), ++ strsize2) && ++ !efi_guidcmp(entry->var.VendorGuid, ++ *vendor)) { ++ found = true; ++ break; ++ } ++ } ++ return found; ++} ++ + /* + * Returns the size of variable_name, in bytes, including the + * terminating NULL character, or variable_name_size if no NULL +@@ -1154,6 +1179,28 @@ void unregister_efivars(struct efivars * + } + EXPORT_SYMBOL_GPL(unregister_efivars); + ++/* ++ * Print a warning when duplicate EFI variables are encountered and ++ * disable the sysfs workqueue since the firmware is buggy. ++ */ ++static void dup_variable_bug(efi_char16_t *s16, efi_guid_t *vendor_guid, ++ unsigned long len16) ++{ ++ size_t i, len8 = len16 / sizeof(efi_char16_t); ++ char *s8; ++ ++ s8 = kzalloc(len8, GFP_KERNEL); ++ if (!s8) ++ return; ++ ++ for (i = 0; i < len8; i++) ++ s8[i] = s16[i]; ++ ++ printk(KERN_WARNING "efivars: duplicate variable: %s-%pUl\n", ++ s8, vendor_guid); ++ kfree(s8); ++} ++ + int register_efivars(struct efivars *efivars, + const struct efivar_operations *ops, + struct kobject *parent_kobj) +@@ -1196,6 +1243,22 @@ int register_efivars(struct efivars *efi + case EFI_SUCCESS: + variable_name_size = var_name_strnsize(variable_name, + variable_name_size); ++ ++ /* ++ * Some firmware implementations return the ++ * same variable name on multiple calls to ++ * get_next_variable(). Terminate the loop ++ * immediately as there is no guarantee that ++ * we'll ever see a different variable name, ++ * and may end up looping here forever. ++ */ ++ if (variable_is_present(variable_name, &vendor_guid)) { ++ dup_variable_bug(variable_name, &vendor_guid, ++ variable_name_size); ++ status = EFI_NOT_FOUND; ++ break; ++ } ++ + efivar_create_sysfs_entry(efivars, + variable_name_size, + variable_name, +@@ -1232,9 +1295,6 @@ out: + } + EXPORT_SYMBOL_GPL(register_efivars); + +-static struct efivars __efivars; +-static struct efivar_operations ops; +- + /* + * For now we register the efi subsystem with the firmware subsystem + * and the vars subsystem with the efi subsystem. In the future, it diff --git a/queue-3.4/series b/queue-3.4/series index cbfbec0e316..d4164f8a447 100644 --- a/queue-3.4/series +++ b/queue-3.4/series @@ -35,3 +35,5 @@ arm-cns3xxx-fix-mapping-of-private-memory-region.patch nfsd4-reject-negative-acl-lengths.patch drm-i915-don-t-clobber-crtc-fb-when-queue_flip-fails.patch btrfs-fix-space-leak-when-we-fail-to-reserve-metadata-space.patch +efivars-explicitly-calculate-length-of-variablename.patch +efivars-handle-duplicate-names-from-get_next_variable.patch