--- /dev/null
+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 },
+ };
+
--- /dev/null
+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);
+@@ -797,7 +807,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
+@@ -769,8 +769,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];
+@@ -832,7 +834,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);
--- /dev/null
+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
+@@ -130,6 +130,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)
+@@ -147,13 +148,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;
+
+@@ -209,7 +214,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
+@@ -836,6 +836,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");
--- /dev/null
+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];
+@@ -807,7 +821,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
+@@ -834,7 +834,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);
--- /dev/null
+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
+@@ -405,35 +405,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
+@@ -128,7 +128,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);
+@@ -138,15 +138,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] = '-';
+
--- /dev/null
+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);
--- /dev/null
+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;
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