The "unlink" handling should perform list removal (which can also make
sure records don't get double-erased), and the "evict" handling should
be responsible only for memory freeing.

Signed-off-by: Kees Cook <keesc...@chromium.org>
---
 fs/pstore/inode.c | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c
index 92ebcc75434f..5f08b21b7a46 100644
--- a/fs/pstore/inode.c
+++ b/fs/pstore/inode.c
@@ -177,10 +177,21 @@ static int pstore_unlink(struct inode *dir, struct dentry 
*dentry)
 {
        struct pstore_private *p = d_inode(dentry)->i_private;
        struct pstore_record *record = p->record;
+       int rc = 0;
 
        if (!record->psi->erase)
                return -EPERM;
 
+       /* Make sure we can't race while removing this file. */
+       mutex_lock(&records_list_lock);
+       if (!list_empty(&p->list))
+               list_del_init(&p->list);
+       else
+               rc = -ENOENT;
+       mutex_unlock(&records_list_lock);
+       if (rc)
+               return rc;
+
        mutex_lock(&record->psi->read_mutex);
        record->psi->erase(record);
        mutex_unlock(&record->psi->read_mutex);
@@ -193,12 +204,7 @@ static void pstore_evict_inode(struct inode *inode)
        struct pstore_private   *p = inode->i_private;
 
        clear_inode(inode);
-       if (p) {
-               mutex_lock(&records_list_lock);
-               list_del(&p->list);
-               mutex_unlock(&records_list_lock);
-               free_pstore_private(p);
-       }
+       free_pstore_private(p);
 }
 
 static const struct inode_operations pstore_dir_inode_operations = {
@@ -417,6 +423,7 @@ static void pstore_kill_sb(struct super_block *sb)
 {
        kill_litter_super(sb);
        pstore_sb = NULL;
+       INIT_LIST_HEAD(&records_list);
 }
 
 static struct file_system_type pstore_fs_type = {
-- 
2.20.1

Reply via email to