OSDN Git Service

pstore: Add locking around superblock changes
authorKees Cook <keescook@chromium.org>
Tue, 5 May 2020 02:43:41 +0000 (19:43 -0700)
committerKees Cook <keescook@chromium.org>
Sat, 30 May 2020 17:33:46 +0000 (10:33 -0700)
Nothing was protecting changes to the pstorefs superblock. Add locking
and refactor away is_pstore_mounted(), instead using a helper to add a
way to safely lock the pstorefs root inode during filesystem changes.

Link: https://lore.kernel.org/lkml/20200506152114.50375-9-keescook@chromium.org/
Signed-off-by: Kees Cook <keescook@chromium.org>
fs/pstore/inode.c
fs/pstore/internal.h
fs/pstore/platform.c

index 5f08b21..0e76e12 100644 (file)
@@ -31,6 +31,9 @@
 static DEFINE_MUTEX(records_list_lock);
 static LIST_HEAD(records_list);
 
+static DEFINE_MUTEX(pstore_sb_lock);
+static struct super_block *pstore_sb;
+
 struct pstore_private {
        struct list_head list;
        struct pstore_record *record;
@@ -282,11 +285,25 @@ static const struct super_operations pstore_ops = {
        .show_options   = pstore_show_options,
 };
 
-static struct super_block *pstore_sb;
-
-bool pstore_is_mounted(void)
+static struct dentry *psinfo_lock_root(void)
 {
-       return pstore_sb != NULL;
+       struct dentry *root;
+
+       mutex_lock(&pstore_sb_lock);
+       /*
+        * Having no backend is fine -- no records appear.
+        * Not being mounted is fine -- nothing to do.
+        */
+       if (!psinfo || !pstore_sb) {
+               mutex_unlock(&pstore_sb_lock);
+               return NULL;
+       }
+
+       root = pstore_sb->s_root;
+       inode_lock(d_inode(root));
+       mutex_unlock(&pstore_sb_lock);
+
+       return root;
 }
 
 /*
@@ -303,20 +320,18 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
        struct pstore_private   *private, *pos;
        size_t                  size = record->size + record->ecc_notice_size;
 
-       WARN_ON(!inode_is_locked(d_inode(root)));
+       if (WARN_ON(!inode_is_locked(d_inode(root))))
+               return -EINVAL;
 
+       rc = -EEXIST;
+       /* Skip records that are already present in the filesystem. */
        mutex_lock(&records_list_lock);
        list_for_each_entry(pos, &records_list, list) {
                if (pos->record->type == record->type &&
                    pos->record->id == record->id &&
-                   pos->record->psi == record->psi) {
-                       rc = -EEXIST;
-                       break;
-               }
+                   pos->record->psi == record->psi)
+                       goto fail;
        }
-       mutex_unlock(&records_list_lock);
-       if (rc)
-               return rc;
 
        rc = -ENOMEM;
        inode = pstore_get_inode(root->d_sb);
@@ -346,7 +361,6 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
 
        d_add(dentry, inode);
 
-       mutex_lock(&records_list_lock);
        list_add(&private->list, &records_list);
        mutex_unlock(&records_list_lock);
 
@@ -356,8 +370,8 @@ fail_private:
        free_pstore_private(private);
 fail_inode:
        iput(inode);
-
 fail:
+       mutex_unlock(&records_list_lock);
        return rc;
 }
 
@@ -369,16 +383,13 @@ fail:
  */
 void pstore_get_records(int quiet)
 {
-       struct pstore_info *psi = psinfo;
        struct dentry *root;
 
-       if (!psi || !pstore_sb)
+       root = psinfo_lock_root();
+       if (!root)
                return;
 
-       root = pstore_sb->s_root;
-
-       inode_lock(d_inode(root));
-       pstore_get_backend_records(psi, root, quiet);
+       pstore_get_backend_records(psinfo, root, quiet);
        inode_unlock(d_inode(root));
 }
 
@@ -386,8 +397,6 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent)
 {
        struct inode *inode;
 
-       pstore_sb = sb;
-
        sb->s_maxbytes          = MAX_LFS_FILESIZE;
        sb->s_blocksize         = PAGE_SIZE;
        sb->s_blocksize_bits    = PAGE_SHIFT;
@@ -408,6 +417,10 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent)
        if (!sb->s_root)
                return -ENOMEM;
 
+       mutex_lock(&pstore_sb_lock);
+       pstore_sb = sb;
+       mutex_unlock(&pstore_sb_lock);
+
        pstore_get_records(0);
 
        return 0;
@@ -421,9 +434,17 @@ static struct dentry *pstore_mount(struct file_system_type *fs_type,
 
 static void pstore_kill_sb(struct super_block *sb)
 {
+       mutex_lock(&pstore_sb_lock);
+       WARN_ON(pstore_sb != sb);
+
        kill_litter_super(sb);
        pstore_sb = NULL;
+
+       mutex_lock(&records_list_lock);
        INIT_LIST_HEAD(&records_list);
+       mutex_unlock(&records_list_lock);
+
+       mutex_unlock(&pstore_sb_lock);
 }
 
 static struct file_system_type pstore_fs_type = {
index 7062ea4..fe5f7ef 100644 (file)
@@ -33,7 +33,6 @@ extern void   pstore_get_backend_records(struct pstore_info *psi,
                                           struct dentry *root, int quiet);
 extern int     pstore_mkfile(struct dentry *root,
                              struct pstore_record *record);
-extern bool    pstore_is_mounted(void);
 extern void    pstore_record_init(struct pstore_record *record,
                                   struct pstore_info *psi);
 
index 8c0076a..624e100 100644 (file)
@@ -460,7 +460,7 @@ static void pstore_dump(struct kmsg_dumper *dumper,
                }
 
                ret = psinfo->write(&record);
-               if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted())
+               if (ret == 0 && reason == KMSG_DUMP_OOPS)
                        pstore_new_entry = 1;
 
                total += record.size;
@@ -592,8 +592,7 @@ int pstore_register(struct pstore_info *psi)
        if (psi->flags & PSTORE_FLAGS_DMESG)
                allocate_buf_for_compression();
 
-       if (pstore_is_mounted())
-               pstore_get_records(0);
+       pstore_get_records(0);
 
        if (psi->flags & PSTORE_FLAGS_DMESG)
                pstore_register_kmsg();