]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.4-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 2 Apr 2013 20:20:31 +0000 (13:20 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 2 Apr 2013 20:20:31 +0000 (13:20 -0700)
added patches:
efivars-explicitly-calculate-length-of-variablename.patch
efivars-handle-duplicate-names-from-get_next_variable.patch

queue-3.4/efivars-explicitly-calculate-length-of-variablename.patch [new file with mode: 0644]
queue-3.4/efivars-handle-duplicate-names-from-get_next_variable.patch [new file with mode: 0644]
queue-3.4/series

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 (file)
index 0000000..291ff90
--- /dev/null
@@ -0,0 +1,96 @@
+From ec50bd32f1672d38ddce10fb1841cbfda89cfe9a Mon Sep 17 00:00:00 2001
+From: Matt Fleming <matt.fleming@intel.com>
+Date: Fri, 1 Mar 2013 14:49:12 +0000
+Subject: efivars: explicitly calculate length of VariableName
+
+From: Matt Fleming <matt.fleming@intel.com>
+
+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 <fcrozat@suse.com>
+Cc: Matthew Garrett <mjg59@srcf.ucam.org>
+Cc: Josh Boyer <jwboyer@redhat.com>
+Cc: Michael Schroeder <mls@suse.com>
+Cc: Lee, Chun-Yi <jlee@suse.com>
+Cc: Lingzhu Xiang <lxiang@redhat.com>
+Cc: Seiji Aguchi <seiji.aguchi@hds.com>
+Signed-off-by: Matt Fleming <matt.fleming@intel.com>
+Signed-off-by: Lingzhu Xiang <lxiang@redhat.com>
+Reviewed-by: CAI Qian <caiqian@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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 (file)
index 0000000..68d0f6c
--- /dev/null
@@ -0,0 +1,192 @@
+From e971318bbed610e28bb3fde9d548e6aaf0a6b02e Mon Sep 17 00:00:00 2001
+From: Matt Fleming <matt.fleming@intel.com>
+Date: Thu, 7 Mar 2013 11:59:14 +0000
+Subject: efivars: Handle duplicate names from get_next_variable()
+
+From: Matt Fleming <matt.fleming@intel.com>
+
+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]  [<ffffffff81208954>] ? sysfs_add_one+0xd4/0x100
+[    0.591861]  [<ffffffff810587bf>] warn_slowpath_common+0x7f/0xc0
+[    0.592650]  [<ffffffff810588bc>] warn_slowpath_fmt+0x4c/0x50
+[    0.593429]  [<ffffffff8134dd85>] ? strlcat+0x65/0x80
+[    0.594203]  [<ffffffff81208954>] sysfs_add_one+0xd4/0x100
+[    0.594979]  [<ffffffff81208b78>] create_dir+0x78/0xd0
+[    0.595753]  [<ffffffff81208ec6>] sysfs_create_dir+0x86/0xe0
+[    0.596532]  [<ffffffff81347e4c>] kobject_add_internal+0x9c/0x220
+[    0.597310]  [<ffffffff81348307>] kobject_init_and_add+0x67/0x90
+[    0.598083]  [<ffffffff81584a71>] ? efivar_create_sysfs_entry+0x61/0x1c0
+[    0.598859]  [<ffffffff81584b2b>] efivar_create_sysfs_entry+0x11b/0x1c0
+[    0.599631]  [<ffffffff8158517e>] register_efivars+0xde/0x420
+[    0.600395]  [<ffffffff81d430a7>] ? edd_init+0x2f5/0x2f5
+[    0.601150]  [<ffffffff81d4315f>] efivars_init+0xb8/0x104
+[    0.601903]  [<ffffffff8100215a>] do_one_initcall+0x12a/0x180
+[    0.602659]  [<ffffffff81d05d80>] kernel_init_freeable+0x13e/0x1c6
+[    0.603418]  [<ffffffff81d05586>] ? loglevel+0x31/0x31
+[    0.604183]  [<ffffffff816a6530>] ? rest_init+0x80/0x80
+[    0.604936]  [<ffffffff816a653e>] kernel_init+0xe/0xf0
+[    0.605681]  [<ffffffff816ce7ec>] ret_from_fork+0x7c/0xb0
+[    0.606414]  [<ffffffff816a6530>] ? 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 <a.heider@gmail.com>
+Reported-by: Lingzhu Xiang <lxiang@redhat.com>
+Tested-by: Lingzhu Xiang <lxiang@redhat.com>
+Cc: Seiji Aguchi <seiji.aguchi@hds.com>
+Signed-off-by: Matt Fleming <matt.fleming@intel.com>
+Signed-off-by: Lingzhu Xiang <lxiang@redhat.com>
+Reviewed-by: CAI Qian <caiqian@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+
+---
+ 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
index cbfbec0e31617cafeb8cf8dc5f6839bb0dc0f237..d4164f8a447dc2fa2810f81d8d33509ee414d9e0 100644 (file)
@@ -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