]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.14-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 13 Mar 2016 04:00:37 +0000 (20:00 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 13 Mar 2016 04:00:37 +0000 (20:00 -0800)
added patches:
efi-add-pstore-variables-to-the-deletion-whitelist.patch
efi-do-variable-name-validation-tests-in-utf8.patch
efi-make-efivarfs-entries-immutable-by-default.patch
efi-make-our-variable-validation-list-include-the-guid.patch
efi-use-ucs2_as_utf8-in-efivarfs-instead-of-open-coding-a-bad-version.patch
lib-ucs2_string-add-ucs2-utf8-helper-functions.patch
lib-ucs2_string-correct-ucs2-utf8-conversion.patch

queue-3.14/efi-add-pstore-variables-to-the-deletion-whitelist.patch [new file with mode: 0644]
queue-3.14/efi-do-variable-name-validation-tests-in-utf8.patch [new file with mode: 0644]
queue-3.14/efi-make-efivarfs-entries-immutable-by-default.patch [new file with mode: 0644]
queue-3.14/efi-make-our-variable-validation-list-include-the-guid.patch [new file with mode: 0644]
queue-3.14/efi-use-ucs2_as_utf8-in-efivarfs-instead-of-open-coding-a-bad-version.patch [new file with mode: 0644]
queue-3.14/lib-ucs2_string-add-ucs2-utf8-helper-functions.patch [new file with mode: 0644]
queue-3.14/lib-ucs2_string-correct-ucs2-utf8-conversion.patch [new file with mode: 0644]
queue-3.14/series

diff --git a/queue-3.14/efi-add-pstore-variables-to-the-deletion-whitelist.patch b/queue-3.14/efi-add-pstore-variables-to-the-deletion-whitelist.patch
new file mode 100644 (file)
index 0000000..fede5d8
--- /dev/null
@@ -0,0 +1,57 @@
+From e246eb568bc4cbbdd8a30a3c11151ff9b7ca7312 Mon Sep 17 00:00:00 2001
+From: Matt Fleming <matt@codeblueprint.co.uk>
+Date: Mon, 15 Feb 2016 10:34:05 +0000
+Subject: efi: Add pstore variables to the deletion whitelist
+
+From: Matt Fleming <matt@codeblueprint.co.uk>
+
+commit e246eb568bc4cbbdd8a30a3c11151ff9b7ca7312 upstream.
+
+Laszlo explains why this is a good idea,
+
+ 'This is because the pstore filesystem can be backed by UEFI variables,
+  and (for example) a crash might dump the last kilobytes of the dmesg
+  into a number of pstore entries, each entry backed by a separate UEFI
+  variable in the above GUID namespace, and with a variable name
+  according to the above pattern.
+
+  Please see "drivers/firmware/efi/efi-pstore.c".
+
+  While this patch series will not prevent the user from deleting those
+  UEFI variables via the pstore filesystem (i.e., deleting a pstore fs
+  entry will continue to delete the backing UEFI variable), I think it
+  would be nice to preserve the possibility for the sysadmin to delete
+  Linux-created UEFI variables that carry portions of the crash log,
+  *without* having to mount the pstore filesystem.'
+
+There's also no chance of causing machines to become bricked by
+deleting these variables, which is the whole purpose of excluding
+things from the whitelist.
+
+Use the LINUX_EFI_CRASH_GUID guid and a wildcard '*' for the match so
+that we don't have to update the string in the future if new variable
+name formats are created for crash dump variables.
+
+Reported-by: Laszlo Ersek <lersek@redhat.com>
+Acked-by: Peter Jones <pjones@redhat.com>
+Tested-by: Peter Jones <pjones@redhat.com>
+Cc: Matthew Garrett <mjg59@srcf.ucam.org>
+Cc: "Lee, Chun-Yi" <jlee@suse.com>
+Signed-off-by: Matt Fleming <matt@codeblueprint.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+
+---
+ drivers/firmware/efi/vars.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/firmware/efi/vars.c
++++ b/drivers/firmware/efi/vars.c
+@@ -198,6 +198,7 @@ static const struct variable_validate va
+       { EFI_GLOBAL_VARIABLE_GUID, "OsIndications", NULL },
+       { EFI_GLOBAL_VARIABLE_GUID, "PlatformLang", validate_ascii_string },
+       { EFI_GLOBAL_VARIABLE_GUID, "Timeout", validate_uint16 },
++      { LINUX_EFI_CRASH_GUID, "*", NULL },
+       { NULL_GUID, "", NULL },
+ };
diff --git a/queue-3.14/efi-do-variable-name-validation-tests-in-utf8.patch b/queue-3.14/efi-do-variable-name-validation-tests-in-utf8.patch
new file mode 100644 (file)
index 0000000..b047c87
--- /dev/null
@@ -0,0 +1,215 @@
+From 3dcb1f55dfc7631695e69df4a0d589ce5274bd07 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 8 Feb 2016 14:48:13 -0500
+Subject: efi: Do variable name validation tests in utf8
+
+From: Peter Jones <pjones@redhat.com>
+
+commit 3dcb1f55dfc7631695e69df4a0d589ce5274bd07 upstream.
+
+Actually translate from ucs2 to utf8 before doing the test, and then
+test against our other utf8 data, instead of fudging it.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+Acked-by: Matthew Garrett <mjg59@coreos.com>
+Tested-by: Lee, Chun-Yi <jlee@suse.com>
+Signed-off-by: Matt Fleming <matt@codeblueprint.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/firmware/efi/efivars.c |    4 +-
+ drivers/firmware/efi/vars.c    |   58 ++++++++++++++++++++++++-----------------
+ include/linux/efi.h            |    6 ++--
+ 3 files changed, 40 insertions(+), 28 deletions(-)
+
+--- a/drivers/firmware/efi/efivars.c
++++ b/drivers/firmware/efi/efivars.c
+@@ -219,7 +219,7 @@ efivar_store_raw(struct efivar_entry *en
+       }
+       if ((new_var->Attributes & ~EFI_VARIABLE_MASK) != 0 ||
+-          efivar_validate(new_var, new_var->Data, new_var->DataSize) == false) {
++          efivar_validate(new_var->VariableName, new_var->Data, new_var->DataSize) == false) {
+               printk(KERN_ERR "efivars: Malformed variable content\n");
+               return -EINVAL;
+       }
+@@ -334,7 +334,7 @@ static ssize_t efivar_create(struct file
+               return -EACCES;
+       if ((new_var->Attributes & ~EFI_VARIABLE_MASK) != 0 ||
+-          efivar_validate(new_var, new_var->Data, new_var->DataSize) == false) {
++          efivar_validate(new_var->VariableName, new_var->Data, new_var->DataSize) == false) {
+               printk(KERN_ERR "efivars: Malformed variable content\n");
+               return -EINVAL;
+       }
+--- a/drivers/firmware/efi/vars.c
++++ b/drivers/firmware/efi/vars.c
+@@ -42,7 +42,7 @@ DECLARE_WORK(efivar_work, NULL);
+ EXPORT_SYMBOL_GPL(efivar_work);
+ static bool
+-validate_device_path(struct efi_variable *var, int match, u8 *buffer,
++validate_device_path(efi_char16_t *var_name, int match, u8 *buffer,
+                    unsigned long len)
+ {
+       struct efi_generic_dev_path *node;
+@@ -75,7 +75,7 @@ validate_device_path(struct efi_variable
+ }
+ static bool
+-validate_boot_order(struct efi_variable *var, int match, u8 *buffer,
++validate_boot_order(efi_char16_t *var_name, int match, u8 *buffer,
+                   unsigned long len)
+ {
+       /* An array of 16-bit integers */
+@@ -86,18 +86,18 @@ validate_boot_order(struct efi_variable
+ }
+ static bool
+-validate_load_option(struct efi_variable *var, int match, u8 *buffer,
++validate_load_option(efi_char16_t *var_name, int match, u8 *buffer,
+                    unsigned long len)
+ {
+       u16 filepathlength;
+       int i, desclength = 0, namelen;
+-      namelen = ucs2_strnlen(var->VariableName, sizeof(var->VariableName));
++      namelen = ucs2_strnlen(var_name, EFI_VAR_NAME_LEN);
+       /* Either "Boot" or "Driver" followed by four digits of hex */
+       for (i = match; i < match+4; i++) {
+-              if (var->VariableName[i] > 127 ||
+-                  hex_to_bin(var->VariableName[i] & 0xff) < 0)
++              if (var_name[i] > 127 ||
++                  hex_to_bin(var_name[i] & 0xff) < 0)
+                       return true;
+       }
+@@ -132,12 +132,12 @@ validate_load_option(struct efi_variable
+       /*
+        * And, finally, check the filepath
+        */
+-      return validate_device_path(var, match, buffer + desclength + 6,
++      return validate_device_path(var_name, match, buffer + desclength + 6,
+                                   filepathlength);
+ }
+ static bool
+-validate_uint16(struct efi_variable *var, int match, u8 *buffer,
++validate_uint16(efi_char16_t *var_name, int match, u8 *buffer,
+               unsigned long len)
+ {
+       /* A single 16-bit integer */
+@@ -148,7 +148,7 @@ validate_uint16(struct efi_variable *var
+ }
+ static bool
+-validate_ascii_string(struct efi_variable *var, int match, u8 *buffer,
++validate_ascii_string(efi_char16_t *var_name, int match, u8 *buffer,
+                     unsigned long len)
+ {
+       int i;
+@@ -166,7 +166,7 @@ validate_ascii_string(struct efi_variabl
+ struct variable_validate {
+       char *name;
+-      bool (*validate)(struct efi_variable *var, int match, u8 *data,
++      bool (*validate)(efi_char16_t *var_name, int match, u8 *data,
+                        unsigned long len);
+ };
+@@ -189,10 +189,19 @@ static const struct variable_validate va
+ };
+ bool
+-efivar_validate(struct efi_variable *var, u8 *data, unsigned long len)
++efivar_validate(efi_char16_t *var_name, u8 *data, unsigned long data_size)
+ {
+       int i;
+-      u16 *unicode_name = var->VariableName;
++      unsigned long utf8_size;
++      u8 *utf8_name;
++
++      utf8_size = ucs2_utf8size(var_name);
++      utf8_name = kmalloc(utf8_size + 1, GFP_KERNEL);
++      if (!utf8_name)
++              return false;
++
++      ucs2_as_utf8(utf8_name, var_name, utf8_size);
++      utf8_name[utf8_size] = '\0';
+       for (i = 0; variable_validate[i].validate != NULL; i++) {
+               const char *name = variable_validate[i].name;
+@@ -200,28 +209,29 @@ efivar_validate(struct efi_variable *var
+               for (match = 0; ; match++) {
+                       char c = name[match];
+-                      u16 u = unicode_name[match];
+-
+-                      /* All special variables are plain ascii */
+-                      if (u > 127)
+-                              return true;
++                      char u = utf8_name[match];
+                       /* Wildcard in the matching name means we've matched */
+-                      if (c == '*')
+-                              return variable_validate[i].validate(var,
+-                                                           match, data, len);
++                      if (c == '*') {
++                              kfree(utf8_name);
++                              return variable_validate[i].validate(var_name,
++                                                      match, data, data_size);
++                      }
+                       /* Case sensitive match */
+                       if (c != u)
+                               break;
+                       /* Reached the end of the string while matching */
+-                      if (!c)
+-                              return variable_validate[i].validate(var,
+-                                                           match, data, len);
++                      if (!c) {
++                              kfree(utf8_name);
++                              return variable_validate[i].validate(var_name,
++                                                      match, data, data_size);
++                      }
+               }
+       }
++      kfree(utf8_name);
+       return true;
+ }
+ EXPORT_SYMBOL_GPL(efivar_validate);
+@@ -805,7 +815,7 @@ int efivar_entry_set_get_size(struct efi
+       *set = false;
+-      if (efivar_validate(&entry->var, data, *size) == false)
++      if (efivar_validate(name, data, *size) == false)
+               return -EINVAL;
+       /*
+--- a/include/linux/efi.h
++++ b/include/linux/efi.h
+@@ -792,8 +792,10 @@ struct efivars {
+  * and we use a page for reading/writing.
+  */
++#define EFI_VAR_NAME_LEN      1024
++
+ struct efi_variable {
+-      efi_char16_t  VariableName[1024/sizeof(efi_char16_t)];
++      efi_char16_t  VariableName[EFI_VAR_NAME_LEN/sizeof(efi_char16_t)];
+       efi_guid_t    VendorGuid;
+       unsigned long DataSize;
+       __u8          Data[1024];
+@@ -864,7 +866,7 @@ int efivar_entry_iter(int (*func)(struct
+ struct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid,
+                                      struct list_head *head, bool remove);
+-bool efivar_validate(struct efi_variable *var, u8 *data, unsigned long len);
++bool efivar_validate(efi_char16_t *var_name, u8 *data, unsigned long len);
+ extern struct work_struct efivar_work;
+ void efivar_run_worker(void);
diff --git a/queue-3.14/efi-make-efivarfs-entries-immutable-by-default.patch b/queue-3.14/efi-make-efivarfs-entries-immutable-by-default.patch
new file mode 100644 (file)
index 0000000..49792a0
--- /dev/null
@@ -0,0 +1,550 @@
+From ed8b0de5a33d2a2557dce7f9429dca8cb5bc5879 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 8 Feb 2016 14:48:15 -0500
+Subject: efi: Make efivarfs entries immutable by default
+
+From: Peter Jones <pjones@redhat.com>
+
+commit ed8b0de5a33d2a2557dce7f9429dca8cb5bc5879 upstream.
+
+"rm -rf" is bricking some peoples' laptops because of variables being
+used to store non-reinitializable firmware driver data that's required
+to POST the hardware.
+
+These are 100% bugs, and they need to be fixed, but in the mean time it
+shouldn't be easy to *accidentally* brick machines.
+
+We have to have delete working, and picking which variables do and don't
+work for deletion is quite intractable, so instead make everything
+immutable by default (except for a whitelist), and make tools that
+aren't quite so broad-spectrum unset the immutable flag.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+Tested-by: Lee, Chun-Yi <jlee@suse.com>
+Acked-by: Matthew Garrett <mjg59@coreos.com>
+Signed-off-by: Matt Fleming <matt@codeblueprint.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ Documentation/filesystems/efivarfs.txt         |    7 ++
+ drivers/firmware/efi/vars.c                    |   87 ++++++++++++++++++-------
+ fs/efivarfs/file.c                             |   71 ++++++++++++++++++++
+ fs/efivarfs/inode.c                            |   30 +++++---
+ fs/efivarfs/internal.h                         |    3 
+ fs/efivarfs/super.c                            |    9 ++
+ include/linux/efi.h                            |    2 
+ tools/testing/selftests/efivarfs/efivarfs.sh   |   19 ++++-
+ tools/testing/selftests/efivarfs/open-unlink.c |   72 ++++++++++++++++++++
+ 9 files changed, 259 insertions(+), 41 deletions(-)
+
+--- a/Documentation/filesystems/efivarfs.txt
++++ b/Documentation/filesystems/efivarfs.txt
+@@ -14,3 +14,10 @@ filesystem.
+ efivarfs is typically mounted like this,
+       mount -t efivarfs none /sys/firmware/efi/efivars
++
++Due to the presence of numerous firmware bugs where removing non-standard
++UEFI variables causes the system firmware to fail to POST, efivarfs
++files that are not well-known standardized variables are created
++as immutable files.  This doesn't prevent removal - "chattr -i" will work -
++but it does prevent this kind of failure from being accomplished
++accidentally.
+--- a/drivers/firmware/efi/vars.c
++++ b/drivers/firmware/efi/vars.c
+@@ -172,10 +172,12 @@ struct variable_validate {
+ };
+ /*
+- * This is the list of variables we need to validate.
++ * This is the list of variables we need to validate, as well as the
++ * whitelist for what we think is safe not to default to immutable.
+  *
+  * If it has a validate() method that's not NULL, it'll go into the
+- * validation routine.  If not, it is assumed valid.
++ * validation routine.  If not, it is assumed valid, but still used for
++ * whitelisting.
+  *
+  * Note that it's sorted by {vendor,name}, but globbed names must come after
+  * any other name with the same prefix.
+@@ -193,11 +195,37 @@ static const struct variable_validate va
+       { EFI_GLOBAL_VARIABLE_GUID, "ErrOut", validate_device_path },
+       { EFI_GLOBAL_VARIABLE_GUID, "ErrOutDev", validate_device_path },
+       { EFI_GLOBAL_VARIABLE_GUID, "Lang", validate_ascii_string },
++      { EFI_GLOBAL_VARIABLE_GUID, "OsIndications", NULL },
+       { EFI_GLOBAL_VARIABLE_GUID, "PlatformLang", validate_ascii_string },
+       { EFI_GLOBAL_VARIABLE_GUID, "Timeout", validate_uint16 },
+       { NULL_GUID, "", NULL },
+ };
++static bool
++variable_matches(const char *var_name, size_t len, const char *match_name,
++               int *match)
++{
++      for (*match = 0; ; (*match)++) {
++              char c = match_name[*match];
++              char u = var_name[*match];
++
++              /* Wildcard in the matching name means we've matched */
++              if (c == '*')
++                      return true;
++
++              /* Case sensitive match */
++              if (!c && *match == len)
++                      return true;
++
++              if (c != u)
++                      return false;
++
++              if (!c)
++                      return true;
++      }
++      return true;
++}
++
+ bool
+ efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data,
+               unsigned long data_size)
+@@ -221,35 +249,48 @@ efivar_validate(efi_guid_t vendor, efi_c
+               if (efi_guidcmp(vendor, variable_validate[i].vendor))
+                       continue;
+-              for (match = 0; ; match++) {
+-                      char c = name[match];
+-                      char u = utf8_name[match];
+-
+-                      /* Wildcard in the matching name means we've matched */
+-                      if (c == '*') {
+-                              kfree(utf8_name);
+-                              return variable_validate[i].validate(var_name,
+-                                                      match, data, data_size);
+-                      }
+-
+-                      /* Case sensitive match */
+-                      if (c != u)
++              if (variable_matches(utf8_name, utf8_size+1, name, &match)) {
++                      if (variable_validate[i].validate == NULL)
+                               break;
+-
+-                      /* Reached the end of the string while matching */
+-                      if (!c) {
+-                              kfree(utf8_name);
+-                              return variable_validate[i].validate(var_name,
+-                                                      match, data, data_size);
+-                      }
++                      kfree(utf8_name);
++                      return variable_validate[i].validate(var_name, match,
++                                                           data, data_size);
+               }
+       }
+-
+       kfree(utf8_name);
+       return true;
+ }
+ EXPORT_SYMBOL_GPL(efivar_validate);
++bool
++efivar_variable_is_removable(efi_guid_t vendor, const char *var_name,
++                           size_t len)
++{
++      int i;
++      bool found = false;
++      int match = 0;
++
++      /*
++       * Check if our variable is in the validated variables list
++       */
++      for (i = 0; variable_validate[i].name[0] != '\0'; i++) {
++              if (efi_guidcmp(variable_validate[i].vendor, vendor))
++                      continue;
++
++              if (variable_matches(var_name, len,
++                                   variable_validate[i].name, &match)) {
++                      found = true;
++                      break;
++              }
++      }
++
++      /*
++       * If it's in our list, it is removable.
++       */
++      return found;
++}
++EXPORT_SYMBOL_GPL(efivar_variable_is_removable);
++
+ static efi_status_t
+ check_var_size(u32 attributes, unsigned long size)
+ {
+--- a/fs/efivarfs/file.c
++++ b/fs/efivarfs/file.c
+@@ -10,6 +10,7 @@
+ #include <linux/efi.h>
+ #include <linux/fs.h>
+ #include <linux/slab.h>
++#include <linux/mount.h>
+ #include "internal.h"
+@@ -108,9 +109,79 @@ out_free:
+       return size;
+ }
++static int
++efivarfs_ioc_getxflags(struct file *file, void __user *arg)
++{
++      struct inode *inode = file->f_mapping->host;
++      unsigned int i_flags;
++      unsigned int flags = 0;
++
++      i_flags = inode->i_flags;
++      if (i_flags & S_IMMUTABLE)
++              flags |= FS_IMMUTABLE_FL;
++
++      if (copy_to_user(arg, &flags, sizeof(flags)))
++              return -EFAULT;
++      return 0;
++}
++
++static int
++efivarfs_ioc_setxflags(struct file *file, void __user *arg)
++{
++      struct inode *inode = file->f_mapping->host;
++      unsigned int flags;
++      unsigned int i_flags = 0;
++      int error;
++
++      if (!inode_owner_or_capable(inode))
++              return -EACCES;
++
++      if (copy_from_user(&flags, arg, sizeof(flags)))
++              return -EFAULT;
++
++      if (flags & ~FS_IMMUTABLE_FL)
++              return -EOPNOTSUPP;
++
++      if (!capable(CAP_LINUX_IMMUTABLE))
++              return -EPERM;
++
++      if (flags & FS_IMMUTABLE_FL)
++              i_flags |= S_IMMUTABLE;
++
++
++      error = mnt_want_write_file(file);
++      if (error)
++              return error;
++
++      mutex_lock(&inode->i_mutex);
++      inode->i_flags &= ~S_IMMUTABLE;
++      inode->i_flags |= i_flags;
++      mutex_unlock(&inode->i_mutex);
++
++      mnt_drop_write_file(file);
++
++      return 0;
++}
++
++long
++efivarfs_file_ioctl(struct file *file, unsigned int cmd, unsigned long p)
++{
++      void __user *arg = (void __user *)p;
++
++      switch (cmd) {
++      case FS_IOC_GETFLAGS:
++              return efivarfs_ioc_getxflags(file, arg);
++      case FS_IOC_SETFLAGS:
++              return efivarfs_ioc_setxflags(file, arg);
++      }
++
++      return -ENOTTY;
++}
++
+ const struct file_operations efivarfs_file_operations = {
+       .open   = simple_open,
+       .read   = efivarfs_file_read,
+       .write  = efivarfs_file_write,
+       .llseek = no_llseek,
++      .unlocked_ioctl = efivarfs_file_ioctl,
+ };
+--- a/fs/efivarfs/inode.c
++++ b/fs/efivarfs/inode.c
+@@ -15,7 +15,8 @@
+ #include "internal.h"
+ struct inode *efivarfs_get_inode(struct super_block *sb,
+-                              const struct inode *dir, int mode, dev_t dev)
++                              const struct inode *dir, int mode,
++                              dev_t dev, bool is_removable)
+ {
+       struct inode *inode = new_inode(sb);
+@@ -23,6 +24,7 @@ struct inode *efivarfs_get_inode(struct
+               inode->i_ino = get_next_ino();
+               inode->i_mode = mode;
+               inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
++              inode->i_flags = is_removable ? 0 : S_IMMUTABLE;
+               switch (mode & S_IFMT) {
+               case S_IFREG:
+                       inode->i_fop = &efivarfs_file_operations;
+@@ -102,22 +104,17 @@ static void efivarfs_hex_to_guid(const c
+ static int efivarfs_create(struct inode *dir, struct dentry *dentry,
+                         umode_t mode, bool excl)
+ {
+-      struct inode *inode;
++      struct inode *inode = NULL;
+       struct efivar_entry *var;
+       int namelen, i = 0, err = 0;
++      bool is_removable = false;
+       if (!efivarfs_valid_name(dentry->d_name.name, dentry->d_name.len))
+               return -EINVAL;
+-      inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0);
+-      if (!inode)
+-              return -ENOMEM;
+-
+       var = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL);
+-      if (!var) {
+-              err = -ENOMEM;
+-              goto out;
+-      }
++      if (!var)
++              return -ENOMEM;
+       /* length of the variable name itself: remove GUID and separator */
+       namelen = dentry->d_name.len - EFI_VARIABLE_GUID_LEN - 1;
+@@ -125,6 +122,16 @@ static int efivarfs_create(struct inode
+       efivarfs_hex_to_guid(dentry->d_name.name + namelen + 1,
+                       &var->var.VendorGuid);
++      if (efivar_variable_is_removable(var->var.VendorGuid,
++                                       dentry->d_name.name, namelen))
++              is_removable = true;
++
++      inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0, is_removable);
++      if (!inode) {
++              err = -ENOMEM;
++              goto out;
++      }
++
+       for (i = 0; i < namelen; i++)
+               var->var.VariableName[i] = dentry->d_name.name[i];
+@@ -138,7 +145,8 @@ static int efivarfs_create(struct inode
+ out:
+       if (err) {
+               kfree(var);
+-              iput(inode);
++              if (inode)
++                      iput(inode);
+       }
+       return err;
+ }
+--- a/fs/efivarfs/internal.h
++++ b/fs/efivarfs/internal.h
+@@ -15,7 +15,8 @@ extern const struct file_operations efiv
+ extern const struct inode_operations efivarfs_dir_inode_operations;
+ extern bool efivarfs_valid_name(const char *str, int len);
+ extern struct inode *efivarfs_get_inode(struct super_block *sb,
+-                      const struct inode *dir, int mode, dev_t dev);
++                      const struct inode *dir, int mode, dev_t dev,
++                      bool is_removable);
+ extern struct list_head efivarfs_list;
+--- a/fs/efivarfs/super.c
++++ b/fs/efivarfs/super.c
+@@ -120,6 +120,7 @@ static int efivarfs_callback(efi_char16_
+       char *name;
+       int len;
+       int err = -ENOMEM;
++      bool is_removable = false;
+       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       if (!entry)
+@@ -137,13 +138,17 @@ static int efivarfs_callback(efi_char16_
+       ucs2_as_utf8(name, entry->var.VariableName, len);
++      if (efivar_variable_is_removable(entry->var.VendorGuid, name, len))
++              is_removable = true;
++
+       name[len] = '-';
+       efi_guid_unparse(&entry->var.VendorGuid, name + len + 1);
+       name[len + EFI_VARIABLE_GUID_LEN+1] = '\0';
+-      inode = efivarfs_get_inode(sb, root->d_inode, S_IFREG | 0644, 0);
++      inode = efivarfs_get_inode(sb, root->d_inode, S_IFREG | 0644, 0,
++                                 is_removable);
+       if (!inode)
+               goto fail_name;
+@@ -199,7 +204,7 @@ static int efivarfs_fill_super(struct su
+       sb->s_d_op              = &efivarfs_d_ops;
+       sb->s_time_gran         = 1;
+-      inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0);
++      inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0, true);
+       if (!inode)
+               return -ENOMEM;
+       inode->i_op = &efivarfs_dir_inode_operations;
+--- a/include/linux/efi.h
++++ b/include/linux/efi.h
+@@ -868,6 +868,8 @@ struct efivar_entry *efivar_entry_find(e
+ bool efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data,
+                    unsigned long data_size);
++bool efivar_variable_is_removable(efi_guid_t vendor, const char *name,
++                                size_t len);
+ extern struct work_struct efivar_work;
+ void efivar_run_worker(void);
+--- a/tools/testing/selftests/efivarfs/efivarfs.sh
++++ b/tools/testing/selftests/efivarfs/efivarfs.sh
+@@ -88,7 +88,11 @@ test_delete()
+               exit 1
+       fi
+-      rm $file
++      rm $file 2>/dev/null
++      if [ $? -ne 0 ]; then
++              chattr -i $file
++              rm $file
++      fi
+       if [ -e $file ]; then
+               echo "$file couldn't be deleted" >&2
+@@ -111,6 +115,7 @@ test_zero_size_delete()
+               exit 1
+       fi
++      chattr -i $file
+       printf "$attrs" > $file
+       if [ -e $file ]; then
+@@ -141,7 +146,11 @@ test_valid_filenames()
+                       echo "$file could not be created" >&2
+                       ret=1
+               else
+-                      rm $file
++                      rm $file 2>/dev/null
++                      if [ $? -ne 0 ]; then
++                              chattr -i $file
++                              rm $file
++                      fi
+               fi
+       done
+@@ -174,7 +183,11 @@ test_invalid_filenames()
+               if [ -e $file ]; then
+                       echo "Creating $file should have failed" >&2
+-                      rm $file
++                      rm $file 2>/dev/null
++                      if [ $? -ne 0 ]; then
++                              chattr -i $file
++                              rm $file
++                      fi
+                       ret=1
+               fi
+       done
+--- a/tools/testing/selftests/efivarfs/open-unlink.c
++++ b/tools/testing/selftests/efivarfs/open-unlink.c
+@@ -1,10 +1,68 @@
++#include <errno.h>
+ #include <stdio.h>
+ #include <stdint.h>
+ #include <stdlib.h>
+ #include <unistd.h>
++#include <sys/ioctl.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <fcntl.h>
++#include <linux/fs.h>
++
++static int set_immutable(const char *path, int immutable)
++{
++      unsigned int flags;
++      int fd;
++      int rc;
++      int error;
++
++      fd = open(path, O_RDONLY);
++      if (fd < 0)
++              return fd;
++
++      rc = ioctl(fd, FS_IOC_GETFLAGS, &flags);
++      if (rc < 0) {
++              error = errno;
++              close(fd);
++              errno = error;
++              return rc;
++      }
++
++      if (immutable)
++              flags |= FS_IMMUTABLE_FL;
++      else
++              flags &= ~FS_IMMUTABLE_FL;
++
++      rc = ioctl(fd, FS_IOC_SETFLAGS, &flags);
++      error = errno;
++      close(fd);
++      errno = error;
++      return rc;
++}
++
++static int get_immutable(const char *path)
++{
++      unsigned int flags;
++      int fd;
++      int rc;
++      int error;
++
++      fd = open(path, O_RDONLY);
++      if (fd < 0)
++              return fd;
++
++      rc = ioctl(fd, FS_IOC_GETFLAGS, &flags);
++      if (rc < 0) {
++              error = errno;
++              close(fd);
++              errno = error;
++              return rc;
++      }
++      close(fd);
++      if (flags & FS_IMMUTABLE_FL)
++              return 1;
++      return 0;
++}
+ int main(int argc, char **argv)
+ {
+@@ -27,7 +85,7 @@ int main(int argc, char **argv)
+       buf[4] = 0;
+       /* create a test variable */
+-      fd = open(path, O_WRONLY | O_CREAT);
++      fd = open(path, O_WRONLY | O_CREAT, 0600);
+       if (fd < 0) {
+               perror("open(O_WRONLY)");
+               return EXIT_FAILURE;
+@@ -41,6 +99,18 @@ int main(int argc, char **argv)
+       close(fd);
++      rc = get_immutable(path);
++      if (rc < 0) {
++              perror("ioctl(FS_IOC_GETFLAGS)");
++              return EXIT_FAILURE;
++      } else if (rc) {
++              rc = set_immutable(path, 0);
++              if (rc < 0) {
++                      perror("ioctl(FS_IOC_SETFLAGS)");
++                      return EXIT_FAILURE;
++              }
++      }
++
+       fd = open(path, O_RDONLY);
+       if (fd < 0) {
+               perror("open");
diff --git a/queue-3.14/efi-make-our-variable-validation-list-include-the-guid.patch b/queue-3.14/efi-make-our-variable-validation-list-include-the-guid.patch
new file mode 100644 (file)
index 0000000..1d0568d
--- /dev/null
@@ -0,0 +1,148 @@
+From 8282f5d9c17fe15a9e658c06e3f343efae1a2a2f Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 8 Feb 2016 14:48:14 -0500
+Subject: efi: Make our variable validation list include the guid
+
+From: Peter Jones <pjones@redhat.com>
+
+commit 8282f5d9c17fe15a9e658c06e3f343efae1a2a2f upstream.
+
+All the variables in this list so far are defined to be in the global
+namespace in the UEFI spec, so this just further ensures we're
+validating the variables we think we are.
+
+Including the guid for entries will become more important in future
+patches when we decide whether or not to allow deletion of variables
+based on presence in this list.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+Tested-by: Lee, Chun-Yi <jlee@suse.com>
+Acked-by: Matthew Garrett <mjg59@coreos.com>
+Signed-off-by: Matt Fleming <matt@codeblueprint.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/firmware/efi/efivars.c |    6 +++-
+ drivers/firmware/efi/vars.c    |   52 ++++++++++++++++++++++++++---------------
+ include/linux/efi.h            |    3 +-
+ 3 files changed, 39 insertions(+), 22 deletions(-)
+
+--- a/drivers/firmware/efi/efivars.c
++++ b/drivers/firmware/efi/efivars.c
+@@ -219,7 +219,8 @@ efivar_store_raw(struct efivar_entry *en
+       }
+       if ((new_var->Attributes & ~EFI_VARIABLE_MASK) != 0 ||
+-          efivar_validate(new_var->VariableName, new_var->Data, new_var->DataSize) == false) {
++          efivar_validate(new_var->VendorGuid, new_var->VariableName,
++                          new_var->Data, new_var->DataSize) == false) {
+               printk(KERN_ERR "efivars: Malformed variable content\n");
+               return -EINVAL;
+       }
+@@ -334,7 +335,8 @@ static ssize_t efivar_create(struct file
+               return -EACCES;
+       if ((new_var->Attributes & ~EFI_VARIABLE_MASK) != 0 ||
+-          efivar_validate(new_var->VariableName, new_var->Data, new_var->DataSize) == false) {
++          efivar_validate(new_var->VendorGuid, new_var->VariableName,
++                          new_var->Data, new_var->DataSize) == false) {
+               printk(KERN_ERR "efivars: Malformed variable content\n");
+               return -EINVAL;
+       }
+--- a/drivers/firmware/efi/vars.c
++++ b/drivers/firmware/efi/vars.c
+@@ -165,31 +165,42 @@ validate_ascii_string(efi_char16_t *var_
+ }
+ struct variable_validate {
++      efi_guid_t vendor;
+       char *name;
+       bool (*validate)(efi_char16_t *var_name, int match, u8 *data,
+                        unsigned long len);
+ };
++/*
++ * This is the list of variables we need to validate.
++ *
++ * If it has a validate() method that's not NULL, it'll go into the
++ * validation routine.  If not, it is assumed valid.
++ *
++ * Note that it's sorted by {vendor,name}, but globbed names must come after
++ * any other name with the same prefix.
++ */
+ static const struct variable_validate variable_validate[] = {
+-      { "BootNext", validate_uint16 },
+-      { "BootOrder", validate_boot_order },
+-      { "DriverOrder", validate_boot_order },
+-      { "Boot*", validate_load_option },
+-      { "Driver*", validate_load_option },
+-      { "ConIn", validate_device_path },
+-      { "ConInDev", validate_device_path },
+-      { "ConOut", validate_device_path },
+-      { "ConOutDev", validate_device_path },
+-      { "ErrOut", validate_device_path },
+-      { "ErrOutDev", validate_device_path },
+-      { "Timeout", validate_uint16 },
+-      { "Lang", validate_ascii_string },
+-      { "PlatformLang", validate_ascii_string },
+-      { "", NULL },
++      { EFI_GLOBAL_VARIABLE_GUID, "BootNext", validate_uint16 },
++      { EFI_GLOBAL_VARIABLE_GUID, "BootOrder", validate_boot_order },
++      { EFI_GLOBAL_VARIABLE_GUID, "Boot*", validate_load_option },
++      { EFI_GLOBAL_VARIABLE_GUID, "DriverOrder", validate_boot_order },
++      { EFI_GLOBAL_VARIABLE_GUID, "Driver*", validate_load_option },
++      { EFI_GLOBAL_VARIABLE_GUID, "ConIn", validate_device_path },
++      { EFI_GLOBAL_VARIABLE_GUID, "ConInDev", validate_device_path },
++      { EFI_GLOBAL_VARIABLE_GUID, "ConOut", validate_device_path },
++      { EFI_GLOBAL_VARIABLE_GUID, "ConOutDev", validate_device_path },
++      { EFI_GLOBAL_VARIABLE_GUID, "ErrOut", validate_device_path },
++      { EFI_GLOBAL_VARIABLE_GUID, "ErrOutDev", validate_device_path },
++      { EFI_GLOBAL_VARIABLE_GUID, "Lang", validate_ascii_string },
++      { EFI_GLOBAL_VARIABLE_GUID, "PlatformLang", validate_ascii_string },
++      { EFI_GLOBAL_VARIABLE_GUID, "Timeout", validate_uint16 },
++      { NULL_GUID, "", NULL },
+ };
+ bool
+-efivar_validate(efi_char16_t *var_name, u8 *data, unsigned long data_size)
++efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data,
++              unsigned long data_size)
+ {
+       int i;
+       unsigned long utf8_size;
+@@ -203,9 +214,12 @@ efivar_validate(efi_char16_t *var_name,
+       ucs2_as_utf8(utf8_name, var_name, utf8_size);
+       utf8_name[utf8_size] = '\0';
+-      for (i = 0; variable_validate[i].validate != NULL; i++) {
++      for (i = 0; variable_validate[i].name[0] != '\0'; i++) {
+               const char *name = variable_validate[i].name;
+-              int match;
++              int match = 0;
++
++              if (efi_guidcmp(vendor, variable_validate[i].vendor))
++                      continue;
+               for (match = 0; ; match++) {
+                       char c = name[match];
+@@ -815,7 +829,7 @@ int efivar_entry_set_get_size(struct efi
+       *set = false;
+-      if (efivar_validate(name, data, *size) == false)
++      if (efivar_validate(*vendor, name, data, *size) == false)
+               return -EINVAL;
+       /*
+--- a/include/linux/efi.h
++++ b/include/linux/efi.h
+@@ -866,7 +866,8 @@ int efivar_entry_iter(int (*func)(struct
+ struct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid,
+                                      struct list_head *head, bool remove);
+-bool efivar_validate(efi_char16_t *var_name, u8 *data, unsigned long len);
++bool efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data,
++                   unsigned long data_size);
+ extern struct work_struct efivar_work;
+ void efivar_run_worker(void);
diff --git a/queue-3.14/efi-use-ucs2_as_utf8-in-efivarfs-instead-of-open-coding-a-bad-version.patch b/queue-3.14/efi-use-ucs2_as_utf8-in-efivarfs-instead-of-open-coding-a-bad-version.patch
new file mode 100644 (file)
index 0000000..aec03a3
--- /dev/null
@@ -0,0 +1,100 @@
+From e0d64e6a880e64545ad7d55786aa84ab76bac475 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 8 Feb 2016 14:48:12 -0500
+Subject: efi: Use ucs2_as_utf8 in efivarfs instead of open coding a bad version
+
+From: Peter Jones <pjones@redhat.com>
+
+commit e0d64e6a880e64545ad7d55786aa84ab76bac475 upstream.
+
+Translate EFI's UCS-2 variable names to UTF-8 instead of just assuming
+all variable names fit in ASCII.
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+Acked-by: Matthew Garrett <mjg59@coreos.com>
+Tested-by: Lee, Chun-Yi <jlee@suse.com>
+Signed-off-by: Matt Fleming <matt@codeblueprint.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/firmware/efi/efivars.c |   28 ++++++++++------------------
+ fs/efivarfs/super.c            |    7 +++----
+ 2 files changed, 13 insertions(+), 22 deletions(-)
+
+--- a/drivers/firmware/efi/efivars.c
++++ b/drivers/firmware/efi/efivars.c
+@@ -409,35 +409,27 @@ efivar_create_sysfs_entry(struct efivar_
+ {
+       int i, short_name_size;
+       char *short_name;
+-      unsigned long variable_name_size;
+-      efi_char16_t *variable_name;
+-
+-      variable_name = new_var->var.VariableName;
+-      variable_name_size = ucs2_strlen(variable_name) * sizeof(efi_char16_t);
++      unsigned long utf8_name_size;
++      efi_char16_t *variable_name = new_var->var.VariableName;
+       /*
+-       * Length of the variable bytes in ASCII, plus the '-' separator,
++       * Length of the variable bytes in UTF8, plus the '-' separator,
+        * plus the GUID, plus trailing NUL
+        */
+-      short_name_size = variable_name_size / sizeof(efi_char16_t)
+-                              + 1 + EFI_VARIABLE_GUID_LEN + 1;
+-
+-      short_name = kzalloc(short_name_size, GFP_KERNEL);
++      utf8_name_size = ucs2_utf8size(variable_name);
++      short_name_size = utf8_name_size + 1 + EFI_VARIABLE_GUID_LEN + 1;
++      short_name = kmalloc(short_name_size, GFP_KERNEL);
+       if (!short_name)
+               return 1;
+-      /* Convert Unicode to normal chars (assume top bits are 0),
+-         ala UTF-8 */
+-      for (i=0; i < (int)(variable_name_size / sizeof(efi_char16_t)); i++) {
+-              short_name[i] = variable_name[i] & 0xFF;
+-      }
++      ucs2_as_utf8(short_name, variable_name, short_name_size);
++
+       /* This is ugly, but necessary to separate one vendor's
+          private variables from another's.         */
+-
+-      *(short_name + strlen(short_name)) = '-';
++      short_name[utf8_name_size] = '-';
+       efi_guid_unparse(&new_var->var.VendorGuid,
+-                       short_name + strlen(short_name));
++                       short_name + utf8_name_size + 1);
+       new_var->kobj.kset = efivars_kset;
+--- a/fs/efivarfs/super.c
++++ b/fs/efivarfs/super.c
+@@ -118,7 +118,7 @@ static int efivarfs_callback(efi_char16_
+       struct dentry *dentry, *root = sb->s_root;
+       unsigned long size = 0;
+       char *name;
+-      int len, i;
++      int len;
+       int err = -ENOMEM;
+       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+@@ -128,15 +128,14 @@ static int efivarfs_callback(efi_char16_
+       memcpy(entry->var.VariableName, name16, name_size);
+       memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t));
+-      len = ucs2_strlen(entry->var.VariableName);
++      len = ucs2_utf8size(entry->var.VariableName);
+       /* name, plus '-', plus GUID, plus NUL*/
+       name = kmalloc(len + 1 + EFI_VARIABLE_GUID_LEN + 1, GFP_KERNEL);
+       if (!name)
+               goto fail;
+-      for (i = 0; i < len; i++)
+-              name[i] = entry->var.VariableName[i] & 0xFF;
++      ucs2_as_utf8(name, entry->var.VariableName, len);
+       name[len] = '-';
diff --git a/queue-3.14/lib-ucs2_string-add-ucs2-utf8-helper-functions.patch b/queue-3.14/lib-ucs2_string-add-ucs2-utf8-helper-functions.patch
new file mode 100644 (file)
index 0000000..6dbad74
--- /dev/null
@@ -0,0 +1,102 @@
+From 73500267c930baadadb0d02284909731baf151f7 Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Mon, 8 Feb 2016 14:48:11 -0500
+Subject: lib/ucs2_string: Add ucs2 -> utf8 helper functions
+
+From: Peter Jones <pjones@redhat.com>
+
+commit 73500267c930baadadb0d02284909731baf151f7 upstream.
+
+This adds ucs2_utf8size(), which tells us how big our ucs2 string is in
+bytes, and ucs2_as_utf8, which translates from ucs2 to utf8..
+
+Signed-off-by: Peter Jones <pjones@redhat.com>
+Tested-by: Lee, Chun-Yi <jlee@suse.com>
+Acked-by: Matthew Garrett <mjg59@coreos.com>
+Signed-off-by: Matt Fleming <matt@codeblueprint.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ include/linux/ucs2_string.h |    4 ++
+ lib/ucs2_string.c           |   62 ++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 66 insertions(+)
+
+--- a/include/linux/ucs2_string.h
++++ b/include/linux/ucs2_string.h
+@@ -11,4 +11,8 @@ unsigned long ucs2_strlen(const ucs2_cha
+ unsigned long ucs2_strsize(const ucs2_char_t *data, unsigned long maxlength);
+ int ucs2_strncmp(const ucs2_char_t *a, const ucs2_char_t *b, size_t len);
++unsigned long ucs2_utf8size(const ucs2_char_t *src);
++unsigned long ucs2_as_utf8(u8 *dest, const ucs2_char_t *src,
++                         unsigned long maxlength);
++
+ #endif /* _LINUX_UCS2_STRING_H_ */
+--- a/lib/ucs2_string.c
++++ b/lib/ucs2_string.c
+@@ -49,3 +49,65 @@ ucs2_strncmp(const ucs2_char_t *a, const
+         }
+ }
+ EXPORT_SYMBOL(ucs2_strncmp);
++
++unsigned long
++ucs2_utf8size(const ucs2_char_t *src)
++{
++      unsigned long i;
++      unsigned long j = 0;
++
++      for (i = 0; i < ucs2_strlen(src); i++) {
++              u16 c = src[i];
++
++              if (c > 0x800)
++                      j += 3;
++              else if (c > 0x80)
++                      j += 2;
++              else
++                      j += 1;
++      }
++
++      return j;
++}
++EXPORT_SYMBOL(ucs2_utf8size);
++
++/*
++ * copy at most maxlength bytes of whole utf8 characters to dest from the
++ * ucs2 string src.
++ *
++ * The return value is the number of characters copied, not including the
++ * final NUL character.
++ */
++unsigned long
++ucs2_as_utf8(u8 *dest, const ucs2_char_t *src, unsigned long maxlength)
++{
++      unsigned int i;
++      unsigned long j = 0;
++      unsigned long limit = ucs2_strnlen(src, maxlength);
++
++      for (i = 0; maxlength && i < limit; i++) {
++              u16 c = src[i];
++
++              if (c > 0x800) {
++                      if (maxlength < 3)
++                              break;
++                      maxlength -= 3;
++                      dest[j++] = 0xe0 | (c & 0xf000) >> 12;
++                      dest[j++] = 0x80 | (c & 0x0fc0) >> 8;
++                      dest[j++] = 0x80 | (c & 0x003f);
++              } else if (c > 0x80) {
++                      if (maxlength < 2)
++                              break;
++                      maxlength -= 2;
++                      dest[j++] = 0xc0 | (c & 0xfe0) >> 5;
++                      dest[j++] = 0x80 | (c & 0x01f);
++              } else {
++                      maxlength -= 1;
++                      dest[j++] = c & 0x7f;
++              }
++      }
++      if (maxlength)
++              dest[j] = '\0';
++      return j;
++}
++EXPORT_SYMBOL(ucs2_as_utf8);
diff --git a/queue-3.14/lib-ucs2_string-correct-ucs2-utf8-conversion.patch b/queue-3.14/lib-ucs2_string-correct-ucs2-utf8-conversion.patch
new file mode 100644 (file)
index 0000000..104eef6
--- /dev/null
@@ -0,0 +1,69 @@
+From a68075908a37850918ad96b056acc9ac4ce1bd90 Mon Sep 17 00:00:00 2001
+From: Jason Andryuk <jandryuk@gmail.com>
+Date: Fri, 12 Feb 2016 23:13:33 +0000
+Subject: lib/ucs2_string: Correct ucs2 -> utf8 conversion
+
+From: Jason Andryuk <jandryuk@gmail.com>
+
+commit a68075908a37850918ad96b056acc9ac4ce1bd90 upstream.
+
+The comparisons should be >= since 0x800 and 0x80 require an additional bit
+to store.
+
+For the 3 byte case, the existing shift would drop off 2 more bits than
+intended.
+
+For the 2 byte case, there should be 5 bits bits in byte 1, and 6 bits in
+byte 2.
+
+Signed-off-by: Jason Andryuk <jandryuk@gmail.com>
+Reviewed-by: Laszlo Ersek <lersek@redhat.com>
+Cc: Peter Jones <pjones@redhat.com>
+Cc: Matthew Garrett <mjg59@coreos.com>
+Cc: "Lee, Chun-Yi" <jlee@suse.com>
+Signed-off-by: Matt Fleming <matt@codeblueprint.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ lib/ucs2_string.c |   14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+--- a/lib/ucs2_string.c
++++ b/lib/ucs2_string.c
+@@ -59,9 +59,9 @@ ucs2_utf8size(const ucs2_char_t *src)
+       for (i = 0; i < ucs2_strlen(src); i++) {
+               u16 c = src[i];
+-              if (c > 0x800)
++              if (c >= 0x800)
+                       j += 3;
+-              else if (c > 0x80)
++              else if (c >= 0x80)
+                       j += 2;
+               else
+                       j += 1;
+@@ -88,19 +88,19 @@ ucs2_as_utf8(u8 *dest, const ucs2_char_t
+       for (i = 0; maxlength && i < limit; i++) {
+               u16 c = src[i];
+-              if (c > 0x800) {
++              if (c >= 0x800) {
+                       if (maxlength < 3)
+                               break;
+                       maxlength -= 3;
+                       dest[j++] = 0xe0 | (c & 0xf000) >> 12;
+-                      dest[j++] = 0x80 | (c & 0x0fc0) >> 8;
++                      dest[j++] = 0x80 | (c & 0x0fc0) >> 6;
+                       dest[j++] = 0x80 | (c & 0x003f);
+-              } else if (c > 0x80) {
++              } else if (c >= 0x80) {
+                       if (maxlength < 2)
+                               break;
+                       maxlength -= 2;
+-                      dest[j++] = 0xc0 | (c & 0xfe0) >> 5;
+-                      dest[j++] = 0x80 | (c & 0x01f);
++                      dest[j++] = 0xc0 | (c & 0x7c0) >> 6;
++                      dest[j++] = 0x80 | (c & 0x03f);
+               } else {
+                       maxlength -= 1;
+                       dest[j++] = c & 0x7f;
index 5fd779e0b939094f6a1eaf18e7bdebe385ee0116..f47fab0b1e1e193246562ce0f230ce0a3e737dbb 100644 (file)
@@ -9,3 +9,10 @@ mac80211-fix-use-of-uninitialised-values-in-rx-aggregation.patch
 mac80211-minstrel_ht-set-default-tx-aggregation-timeout-to-0.patch
 powerpc-fix-dedotify-for-binutils-2.26.patch
 kvm-x86-move-steal-time-initialization-to-vcpu-entry-time.patch
+lib-ucs2_string-add-ucs2-utf8-helper-functions.patch
+efi-use-ucs2_as_utf8-in-efivarfs-instead-of-open-coding-a-bad-version.patch
+efi-do-variable-name-validation-tests-in-utf8.patch
+efi-make-our-variable-validation-list-include-the-guid.patch
+efi-make-efivarfs-entries-immutable-by-default.patch
+efi-add-pstore-variables-to-the-deletion-whitelist.patch
+lib-ucs2_string-correct-ucs2-utf8-conversion.patch