diff options
author | Ard Biesheuvel <ardb@kernel.org> | 2023-12-08 17:39:29 +0100 |
---|---|---|
committer | Ard Biesheuvel <ardb@kernel.org> | 2023-12-11 11:19:18 +0100 |
commit | cdb46a8aefbf7fd36772bb206aaaf7e45d7cf8f6 (patch) | |
tree | b0eb3387296b811d060af9ce3e5f57b2b9d392ae /fs/efivarfs/super.c | |
parent | 547713d502f7b4b8efccd409cff84d731a23853b (diff) | |
download | linux-cdb46a8aefbf7fd36772bb206aaaf7e45d7cf8f6.tar.gz linux-cdb46a8aefbf7fd36772bb206aaaf7e45d7cf8f6.tar.bz2 linux-cdb46a8aefbf7fd36772bb206aaaf7e45d7cf8f6.zip |
efivarfs: Move efivarfs list into superblock s_fs_info
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>
Diffstat (limited to 'fs/efivarfs/super.c')
-rw-r--r-- | fs/efivarfs/super.c | 19 |
1 files changed, 10 insertions, 9 deletions
diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c index f954b1fab915..cee325b5bbdd 100644 --- a/fs/efivarfs/super.c +++ b/fs/efivarfs/super.c @@ -19,8 +19,6 @@ #include "internal.h" -LIST_HEAD(efivarfs_list); - static void efivarfs_evict_inode(struct inode *inode) { clear_inode(inode); @@ -167,7 +165,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; @@ -222,7 +221,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); @@ -292,6 +291,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 *sfi = sb->s_fs_info; struct inode *inode = NULL; struct dentry *root; int err; @@ -317,11 +317,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, + &sfi->efivarfs_list); if (err) - efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL); + efivar_entry_iter(efivarfs_destroy, &sfi->efivarfs_list, NULL); return err; } @@ -358,6 +357,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; @@ -373,7 +374,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); } |