From: Greg Kroah-Hartman Date: Sun, 13 Mar 2016 04:00:37 +0000 (-0800) Subject: 3.14-stable patches X-Git-Tag: v4.4.6~7 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=be917f66f2d4fc972b5d495c13766a09805142bc;p=thirdparty%2Fkernel%2Fstable-queue.git 3.14-stable patches 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 --- 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 index 00000000000..fede5d8f696 --- /dev/null +++ b/queue-3.14/efi-add-pstore-variables-to-the-deletion-whitelist.patch @@ -0,0 +1,57 @@ +From e246eb568bc4cbbdd8a30a3c11151ff9b7ca7312 Mon Sep 17 00:00:00 2001 +From: Matt Fleming +Date: Mon, 15 Feb 2016 10:34:05 +0000 +Subject: efi: Add pstore variables to the deletion whitelist + +From: Matt Fleming + +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 +Acked-by: Peter Jones +Tested-by: Peter Jones +Cc: Matthew Garrett +Cc: "Lee, Chun-Yi" +Signed-off-by: Matt Fleming +Signed-off-by: Greg Kroah-Hartman + + +--- + 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 index 00000000000..b047c872307 --- /dev/null +++ b/queue-3.14/efi-do-variable-name-validation-tests-in-utf8.patch @@ -0,0 +1,215 @@ +From 3dcb1f55dfc7631695e69df4a0d589ce5274bd07 Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Mon, 8 Feb 2016 14:48:13 -0500 +Subject: efi: Do variable name validation tests in utf8 + +From: Peter Jones + +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 +Acked-by: Matthew Garrett +Tested-by: Lee, Chun-Yi +Signed-off-by: Matt Fleming +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..49792a040a1 --- /dev/null +++ b/queue-3.14/efi-make-efivarfs-entries-immutable-by-default.patch @@ -0,0 +1,550 @@ +From ed8b0de5a33d2a2557dce7f9429dca8cb5bc5879 Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Mon, 8 Feb 2016 14:48:15 -0500 +Subject: efi: Make efivarfs entries immutable by default + +From: Peter Jones + +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 +Tested-by: Lee, Chun-Yi +Acked-by: Matthew Garrett +Signed-off-by: Matt Fleming +Signed-off-by: Greg Kroah-Hartman + +--- + 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 + #include + #include ++#include + + #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 + #include + #include + #include + #include ++#include + #include + #include + #include ++#include ++ ++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 index 00000000000..1d0568def1a --- /dev/null +++ b/queue-3.14/efi-make-our-variable-validation-list-include-the-guid.patch @@ -0,0 +1,148 @@ +From 8282f5d9c17fe15a9e658c06e3f343efae1a2a2f Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Mon, 8 Feb 2016 14:48:14 -0500 +Subject: efi: Make our variable validation list include the guid + +From: Peter Jones + +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 +Tested-by: Lee, Chun-Yi +Acked-by: Matthew Garrett +Signed-off-by: Matt Fleming +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..aec03a37b87 --- /dev/null +++ b/queue-3.14/efi-use-ucs2_as_utf8-in-efivarfs-instead-of-open-coding-a-bad-version.patch @@ -0,0 +1,100 @@ +From e0d64e6a880e64545ad7d55786aa84ab76bac475 Mon Sep 17 00:00:00 2001 +From: Peter Jones +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 + +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 +Acked-by: Matthew Garrett +Tested-by: Lee, Chun-Yi +Signed-off-by: Matt Fleming +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..6dbad7450c3 --- /dev/null +++ b/queue-3.14/lib-ucs2_string-add-ucs2-utf8-helper-functions.patch @@ -0,0 +1,102 @@ +From 73500267c930baadadb0d02284909731baf151f7 Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Mon, 8 Feb 2016 14:48:11 -0500 +Subject: lib/ucs2_string: Add ucs2 -> utf8 helper functions + +From: Peter Jones + +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 +Tested-by: Lee, Chun-Yi +Acked-by: Matthew Garrett +Signed-off-by: Matt Fleming +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..104eef66dc6 --- /dev/null +++ b/queue-3.14/lib-ucs2_string-correct-ucs2-utf8-conversion.patch @@ -0,0 +1,69 @@ +From a68075908a37850918ad96b056acc9ac4ce1bd90 Mon Sep 17 00:00:00 2001 +From: Jason Andryuk +Date: Fri, 12 Feb 2016 23:13:33 +0000 +Subject: lib/ucs2_string: Correct ucs2 -> utf8 conversion + +From: Jason Andryuk + +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 +Reviewed-by: Laszlo Ersek +Cc: Peter Jones +Cc: Matthew Garrett +Cc: "Lee, Chun-Yi" +Signed-off-by: Matt Fleming +Signed-off-by: Greg Kroah-Hartman + +--- + 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; diff --git a/queue-3.14/series b/queue-3.14/series index 5fd779e0b93..f47fab0b1e1 100644 --- a/queue-3.14/series +++ b/queue-3.14/series @@ -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