]> Git Repo - J-linux.git/commitdiff
9p: add missing locking around taking dentry fid list
authorDominique Martinet <[email protected]>
Tue, 21 May 2024 12:13:36 +0000 (21:13 +0900)
committerDominique Martinet <[email protected]>
Thu, 23 May 2024 11:29:09 +0000 (20:29 +0900)
Fix a use-after-free on dentry's d_fsdata fid list when a thread
looks up a fid through dentry while another thread unlinks it:

UAF thread:
refcount_t: addition on 0; use-after-free.
 p9_fid_get linux/./include/net/9p/client.h:262
 v9fs_fid_find+0x236/0x280 linux/fs/9p/fid.c:129
 v9fs_fid_lookup_with_uid linux/fs/9p/fid.c:181
 v9fs_fid_lookup+0xbf/0xc20 linux/fs/9p/fid.c:314
 v9fs_vfs_getattr_dotl+0xf9/0x360 linux/fs/9p/vfs_inode_dotl.c:400
 vfs_statx+0xdd/0x4d0 linux/fs/stat.c:248

Freed by:
 p9_fid_destroy (inlined)
 p9_client_clunk+0xb0/0xe0 linux/net/9p/client.c:1456
 p9_fid_put linux/./include/net/9p/client.h:278
 v9fs_dentry_release+0xb5/0x140 linux/fs/9p/vfs_dentry.c:55
 v9fs_remove+0x38f/0x620 linux/fs/9p/vfs_inode.c:518
 vfs_unlink+0x29a/0x810 linux/fs/namei.c:4335

The problem is that d_fsdata was not accessed under d_lock, because
d_release() normally is only called once the dentry is otherwise no
longer accessible but since we also call it explicitly in v9fs_remove
that lock is required:
move the hlist out of the dentry under lock then unref its fids once
they are no longer accessible.

Fixes: 154372e67d40 ("fs/9p: fix create-unlink-getattr idiom")
Cc: [email protected]
Reported-by: Meysam Firouzi
Reported-by: Amirmohammad Eftekhar
Reviewed-by: Christian Schoenebeck <[email protected]>
Message-ID: <20240521122947.1080227[email protected]>
Signed-off-by: Dominique Martinet <[email protected]>
fs/9p/vfs_dentry.c

index f16f7358163490f633f67d3e93a82eb8da327da5..01338d4c2d9e6fde95e8e69816781ce968522ef6 100644 (file)
@@ -48,12 +48,17 @@ static int v9fs_cached_dentry_delete(const struct dentry *dentry)
 static void v9fs_dentry_release(struct dentry *dentry)
 {
        struct hlist_node *p, *n;
+       struct hlist_head head;
 
        p9_debug(P9_DEBUG_VFS, " dentry: %pd (%p)\n",
                 dentry, dentry);
-       hlist_for_each_safe(p, n, (struct hlist_head *)&dentry->d_fsdata)
+
+       spin_lock(&dentry->d_lock);
+       hlist_move_list((struct hlist_head *)&dentry->d_fsdata, &head);
+       spin_unlock(&dentry->d_lock);
+
+       hlist_for_each_safe(p, n, &head)
                p9_fid_put(hlist_entry(p, struct p9_fid, dlist));
-       dentry->d_fsdata = NULL;
 }
 
 static int v9fs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
This page took 0.050121 seconds and 4 git commands to generate.