Patch "efi: Make efivarfs entries immutable by default" has been added to the 3.14-stable tree
Subsystems (5)
This is a note to let you know that I've just added the patch titled
efi: Make efivarfs entries immutable by default
to the 3.14-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
efi-make-efivarfs-entries-immutable-by-default.patch
and it can be found in the queue-3.14 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.
Quoted 1 line
From
ed8b0de5a33d2a2557dce7f9429dca8cb5bc5879Mon Sep 17 00:00:00 2001
From: Peter Jones <[email protected]>
Date: Mon, 8 Feb 2016 14:48:15 -0500
Subject: efi: Make efivarfs entries immutable by default
From: Peter Jones <[email protected]>
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.
--- 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(-)
Documentation/filesystems/efivarfs.txt +7 -0
@@ -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.
drivers/firmware/efi/vars.c +64 -23
120 lines not on this page · raw message
fs/efivarfs/file.c +71 -0
86 lines not on this page · raw message
fs/efivarfs/inode.c +19 -11
@@ -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 cstatic 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 inodeefivarfs_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; }
fs/efivarfs/internal.h +2 -1
@@ -15,7 +15,8 @@ extern const struct file_operations efivextern 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;
fs/efivarfs/super.c +7 -2
@@ -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;
include/linux/efi.h +2 -0
@@ -868,6 +868,8 @@ struct efivar_entry *efivar_entry_find(ebool 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 );
tools/testing/selftests/efivarfs/efivarfs.sh +16 -3
@@ -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
tools/testing/selftests/efivarfs/open-unlink.c +71 -1
94 lines not on this page · raw message
Patches currently in stable-queue which might be from [email protected] are
queue-3.14/lib-ucs2_string-add-ucs2-utf8-helper-functions.patch queue-3.14/lib-ucs2_string-correct-ucs2-utf8-conversion.patch queue-3.14/efi-do-variable-name-validation-tests-in-utf8.patch queue-3.14/efi-make-efivarfs-entries-immutable-by-default.patch queue-3.14/efi-add-pstore-variables-to-the-deletion-whitelist.patch queue-3.14/efi-make-our-variable-validation-list-include-the-guid.patch queue-3.14/efi-use-ucs2_as_utf8-in-efivarfs-instead-of-open-coding-a-bad-version.patch