linux-efi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] efivarfs fixes
@ 2023-12-08 16:39 Ard Biesheuvel
  2023-12-08 16:39 ` [PATCH 1/3] efivarfs: Move efivar availability check into FS context init Ard Biesheuvel
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Ard Biesheuvel @ 2023-12-08 16:39 UTC (permalink / raw)
  To: linux-efi; +Cc: Ard Biesheuvel, Johan Hovold, Jiao Zhou

From: Ard Biesheuvel <ardb@kernel.org>

Some fixes for efivarfs, most notably, a fix in patch #3 for a long
standing issue reported by syzbot.

Cc: Johan Hovold <johan+linaro@kernel.org>
Cc: Jiao Zhou <jiaozhou@google.com>

Ard Biesheuvel (3):
  efivarfs: Move efivar availability check into FS context init
  efivarfs: Free s_fs_info on unmount
  efivarfs: Move efivarfs list into superblock s_fs_info

 fs/efivarfs/inode.c    |  3 +-
 fs/efivarfs/internal.h |  6 ++--
 fs/efivarfs/super.c    | 31 ++++++++++----------
 fs/efivarfs/vars.c     |  5 ++--
 4 files changed, 24 insertions(+), 21 deletions(-)

-- 
2.43.0.472.g3155946c3a-goog


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/3] efivarfs: Move efivar availability check into FS context init
  2023-12-08 16:39 [PATCH 0/3] efivarfs fixes Ard Biesheuvel
@ 2023-12-08 16:39 ` Ard Biesheuvel
  2023-12-08 16:39 ` [PATCH 2/3] efivarfs: Free s_fs_info on unmount Ard Biesheuvel
  2023-12-08 16:39 ` [PATCH 3/3] efivarfs: Move efivarfs list into superblock s_fs_info Ard Biesheuvel
  2 siblings, 0 replies; 4+ messages in thread
From: Ard Biesheuvel @ 2023-12-08 16:39 UTC (permalink / raw)
  To: linux-efi; +Cc: Ard Biesheuvel, Johan Hovold, Jiao Zhou

From: Ard Biesheuvel <ardb@kernel.org>

Instead of checking whether or not EFI variables are available when
creating the superblock, check it one step earlier, when initializing
the FS context for the mount. This way, no FS context will be created at
all, and we can drop the second check at .kill_sb() time entirely.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 fs/efivarfs/super.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c
index 77240953a92e..72013125d7fa 100644
--- a/fs/efivarfs/super.c
+++ b/fs/efivarfs/super.c
@@ -295,9 +295,6 @@ static int efivarfs_fill_super(struct super_block *sb, struct fs_context *fc)
 	struct dentry *root;
 	int err;
 
-	if (!efivar_is_available())
-		return -EOPNOTSUPP;
-
 	sb->s_maxbytes          = MAX_LFS_FILESIZE;
 	sb->s_blocksize         = PAGE_SIZE;
 	sb->s_blocksize_bits    = PAGE_SHIFT;
@@ -342,6 +339,9 @@ static int efivarfs_init_fs_context(struct fs_context *fc)
 {
 	struct efivarfs_fs_info *sfi;
 
+	if (!efivar_is_available())
+		return -EOPNOTSUPP;
+
 	sfi = kzalloc(sizeof(*sfi), GFP_KERNEL);
 	if (!sfi)
 		return -ENOMEM;
@@ -358,9 +358,6 @@ static void efivarfs_kill_sb(struct super_block *sb)
 {
 	kill_litter_super(sb);
 
-	if (!efivar_is_available())
-		return;
-
 	/* Remove all entries and destroy */
 	efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL);
 }
-- 
2.43.0.472.g3155946c3a-goog


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 2/3] efivarfs: Free s_fs_info on unmount
  2023-12-08 16:39 [PATCH 0/3] efivarfs fixes Ard Biesheuvel
  2023-12-08 16:39 ` [PATCH 1/3] efivarfs: Move efivar availability check into FS context init Ard Biesheuvel
@ 2023-12-08 16:39 ` Ard Biesheuvel
  2023-12-08 16:39 ` [PATCH 3/3] efivarfs: Move efivarfs list into superblock s_fs_info Ard Biesheuvel
  2 siblings, 0 replies; 4+ messages in thread
From: Ard Biesheuvel @ 2023-12-08 16:39 UTC (permalink / raw)
  To: linux-efi; +Cc: Ard Biesheuvel, Johan Hovold, Jiao Zhou

From: Ard Biesheuvel <ardb@kernel.org>

Now that we allocate a s_fs_info struct on fs context creation, we
should ensure that we free it again when the superblock goes away.

Fixes: 5329aa5101f7 ("efivarfs: Add uid/gid mount options")
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 fs/efivarfs/super.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c
index 72013125d7fa..891acf7b0903 100644
--- a/fs/efivarfs/super.c
+++ b/fs/efivarfs/super.c
@@ -356,10 +356,13 @@ static int efivarfs_init_fs_context(struct fs_context *fc)
 
 static void efivarfs_kill_sb(struct super_block *sb)
 {
+	struct efivarfs_fs_info *sfi = sb->s_fs_info;
+
 	kill_litter_super(sb);
 
 	/* Remove all entries and destroy */
 	efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL);
+	kfree(sfi);
 }
 
 static struct file_system_type efivarfs_type = {
-- 
2.43.0.472.g3155946c3a-goog


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 3/3] efivarfs: Move efivarfs list into superblock s_fs_info
  2023-12-08 16:39 [PATCH 0/3] efivarfs fixes Ard Biesheuvel
  2023-12-08 16:39 ` [PATCH 1/3] efivarfs: Move efivar availability check into FS context init Ard Biesheuvel
  2023-12-08 16:39 ` [PATCH 2/3] efivarfs: Free s_fs_info on unmount Ard Biesheuvel
@ 2023-12-08 16:39 ` Ard Biesheuvel
  2 siblings, 0 replies; 4+ messages in thread
From: Ard Biesheuvel @ 2023-12-08 16:39 UTC (permalink / raw)
  To: linux-efi
  Cc: Ard Biesheuvel, Johan Hovold, Jiao Zhou, syzbot+1902c359bfcaf39c46f2

From: Ard Biesheuvel <ardb@kernel.org>

syzbot reports issues with concurrent fsopen()/fsconfig() invocations on
efivarfs, which are the result of the fact that the efivarfs list (which
caches the names and GUIDs of existing EFI variables) is a global
structure. In normal use, these issues are unlikely to trigger, even in
the presence of multiple mounts of efivarfs, but the execution pattern
used by the syzkaller reproducer may result in multiple instances of the
superblock that share the global efivarfs list, and this causes list
corruption when the list is reinitialized by one user while another is
traversing it.

So let's move the list head into the superblock s_fs_info field, so that
it will never be shared between distinct instances of the superblock. In
the common case, there will still be a single instance of this list, but
in the artificial syzkaller case, no list corruption can occur any
longer.

Reported-by: syzbot+1902c359bfcaf39c46f2@syzkaller.appspotmail.com
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 fs/efivarfs/inode.c    |  3 ++-
 fs/efivarfs/internal.h |  6 +++---
 fs/efivarfs/super.c    | 19 ++++++++++---------
 fs/efivarfs/vars.c     |  5 +++--
 4 files changed, 18 insertions(+), 15 deletions(-)

diff --git a/fs/efivarfs/inode.c b/fs/efivarfs/inode.c
index 91290fe4a70b..586446e02ef7 100644
--- a/fs/efivarfs/inode.c
+++ b/fs/efivarfs/inode.c
@@ -77,6 +77,7 @@ bool efivarfs_valid_name(const char *str, int len)
 static int efivarfs_create(struct mnt_idmap *idmap, struct inode *dir,
 			   struct dentry *dentry, umode_t mode, bool excl)
 {
+	struct efivarfs_fs_info *info = dir->i_sb->s_fs_info;
 	struct inode *inode = NULL;
 	struct efivar_entry *var;
 	int namelen, i = 0, err = 0;
@@ -118,7 +119,7 @@ static int efivarfs_create(struct mnt_idmap *idmap, struct inode *dir,
 	inode->i_private = var;
 	kmemleak_ignore(var);
 
-	err = efivar_entry_add(var, &efivarfs_list);
+	err = efivar_entry_add(var, &info->efivarfs_list);
 	if (err)
 		goto out;
 
diff --git a/fs/efivarfs/internal.h b/fs/efivarfs/internal.h
index c66647f5c0bd..1dc0ccce3cc3 100644
--- a/fs/efivarfs/internal.h
+++ b/fs/efivarfs/internal.h
@@ -16,6 +16,7 @@ struct efivarfs_mount_opts {
 
 struct efivarfs_fs_info {
 	struct efivarfs_mount_opts mount_opts;
+	struct list_head efivarfs_list;
 };
 
 struct efi_variable {
@@ -33,7 +34,8 @@ struct efivar_entry {
 	struct kobject kobj;
 };
 
-int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
+int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *,
+			    struct list_head *),
 		void *data, bool duplicates, struct list_head *head);
 
 int efivar_entry_add(struct efivar_entry *entry, struct list_head *head);
@@ -64,6 +66,4 @@ extern struct inode *efivarfs_get_inode(struct super_block *sb,
 			const struct inode *dir, int mode, dev_t dev,
 			bool is_removable);
 
-extern struct list_head efivarfs_list;
-
 #endif /* EFIVAR_FS_INTERNAL_H */
diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c
index 891acf7b0903..d7d9a3e189a0 100644
--- a/fs/efivarfs/super.c
+++ b/fs/efivarfs/super.c
@@ -18,8 +18,6 @@
 
 #include "internal.h"
 
-LIST_HEAD(efivarfs_list);
-
 static void efivarfs_evict_inode(struct inode *inode)
 {
 	clear_inode(inode);
@@ -166,7 +164,8 @@ static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name)
 }
 
 static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
-			     unsigned long name_size, void *data)
+			     unsigned long name_size, void *data,
+			     struct list_head *list)
 {
 	struct super_block *sb = (struct super_block *)data;
 	struct efivar_entry *entry;
@@ -221,7 +220,7 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
 	}
 
 	__efivar_entry_get(entry, NULL, &size, NULL);
-	__efivar_entry_add(entry, &efivarfs_list);
+	__efivar_entry_add(entry, list);
 
 	/* copied by the above to local storage in the dentry. */
 	kfree(name);
@@ -291,6 +290,7 @@ static int efivarfs_parse_param(struct fs_context *fc, struct fs_parameter *para
 
 static int efivarfs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
+	struct efivarfs_fs_info *info = sb->s_fs_info;
 	struct inode *inode = NULL;
 	struct dentry *root;
 	int err;
@@ -316,11 +316,10 @@ static int efivarfs_fill_super(struct super_block *sb, struct fs_context *fc)
 	if (!root)
 		return -ENOMEM;
 
-	INIT_LIST_HEAD(&efivarfs_list);
-
-	err = efivar_init(efivarfs_callback, (void *)sb, true, &efivarfs_list);
+	err = efivar_init(efivarfs_callback, (void *)sb, true,
+			  &info->efivarfs_list);
 	if (err)
-		efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL);
+		efivar_entry_iter(efivarfs_destroy, &info->efivarfs_list, NULL);
 
 	return err;
 }
@@ -346,6 +345,8 @@ static int efivarfs_init_fs_context(struct fs_context *fc)
 	if (!sfi)
 		return -ENOMEM;
 
+	INIT_LIST_HEAD(&sfi->efivarfs_list);
+
 	sfi->mount_opts.uid = GLOBAL_ROOT_UID;
 	sfi->mount_opts.gid = GLOBAL_ROOT_GID;
 
@@ -361,7 +362,7 @@ static void efivarfs_kill_sb(struct super_block *sb)
 	kill_litter_super(sb);
 
 	/* Remove all entries and destroy */
-	efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL);
+	efivar_entry_iter(efivarfs_destroy, &sfi->efivarfs_list, NULL);
 	kfree(sfi);
 }
 
diff --git a/fs/efivarfs/vars.c b/fs/efivarfs/vars.c
index 9e4f47808bd5..114ff0fd4e55 100644
--- a/fs/efivarfs/vars.c
+++ b/fs/efivarfs/vars.c
@@ -369,7 +369,8 @@ static void dup_variable_bug(efi_char16_t *str16, efi_guid_t *vendor_guid,
  *
  * Returns 0 on success, or a kernel error code on failure.
  */
-int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
+int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *,
+			    struct list_head *),
 		void *data, bool duplicates, struct list_head *head)
 {
 	unsigned long variable_name_size = 1024;
@@ -420,7 +421,7 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
 				status = EFI_NOT_FOUND;
 			} else {
 				err = func(variable_name, vendor_guid,
-					   variable_name_size, data);
+					   variable_name_size, data, head);
 				if (err)
 					status = EFI_NOT_FOUND;
 			}
-- 
2.43.0.472.g3155946c3a-goog


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2023-12-08 16:39 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-12-08 16:39 [PATCH 0/3] efivarfs fixes Ard Biesheuvel
2023-12-08 16:39 ` [PATCH 1/3] efivarfs: Move efivar availability check into FS context init Ard Biesheuvel
2023-12-08 16:39 ` [PATCH 2/3] efivarfs: Free s_fs_info on unmount Ard Biesheuvel
2023-12-08 16:39 ` [PATCH 3/3] efivarfs: Move efivarfs list into superblock s_fs_info Ard Biesheuvel

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).