mirror of
https://github.com/libfuse/libfuse.git
synced 2026-07-07 14:47:41 +08:00
memfs_ll: fix memfs_link self-deadlocks and dangling dentry
memfs_link took inodes_mutex exclusively via Inodes.lock(), then resolved the source and parent inodes with Inodes.find(), which takes a shared lock on the same mutex -- a self-deadlock on a non-recursive shared_mutex. It then called parent_inode->add_child(), which re-locks the parent inode mutex already held via parent_inode->lock() -- a second self-deadlock. The new Dentry was owned by a unique_ptr whose raw pointer was handed to the parent and then freed at scope exit, leaving the parent with a dangling dentry. Resolve both inodes with find_locked() (inodes_mutex already held) and link with add_child_locked() (parent inode mutex already held). Heap-allocate the Dentry and hand ownership to the parent, which frees it in remove_child(), matching create()/mkdir(). On add_child_locked() failure, delete the dentry and undo the inc_nlink() before replying the error. Signed-off-by: Bernd Schubert <bernd@bsbernd.com>
This commit is contained in:
+10
-5
@@ -977,17 +977,17 @@ static void memfs_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
|
||||
Inode *src_inode = nullptr;
|
||||
Inode *parent_inode = nullptr;
|
||||
struct fuse_entry_param e;
|
||||
std::unique_ptr<Dentry> new_dentry;
|
||||
Dentry *new_dentry = nullptr;
|
||||
|
||||
Inodes.lock();
|
||||
|
||||
src_inode = Inodes.find(ino);
|
||||
src_inode = Inodes.find_locked(ino);
|
||||
if (!src_inode) {
|
||||
error = ENOENT;
|
||||
goto out_unlock_global;
|
||||
}
|
||||
|
||||
parent_inode = Inodes.find(newparent);
|
||||
parent_inode = Inodes.find_locked(newparent);
|
||||
if (!parent_inode || !parent_inode->is_dir()) {
|
||||
error = ENOENT;
|
||||
goto out_unlock_global;
|
||||
@@ -1003,8 +1003,13 @@ static void memfs_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
|
||||
|
||||
src_inode->inc_nlink();
|
||||
|
||||
new_dentry = std::make_unique<Dentry>(newname, src_inode);
|
||||
parent_inode->add_child(newname, new_dentry.get());
|
||||
new_dentry = new Dentry(newname, src_inode);
|
||||
error = parent_inode->add_child_locked(newname, new_dentry);
|
||||
if (error != 0) {
|
||||
delete new_dentry;
|
||||
src_inode->dec_nlink();
|
||||
goto out_unlock_parent;
|
||||
}
|
||||
|
||||
memset(&e, 0, sizeof(e));
|
||||
e.ino = ino;
|
||||
|
||||
Reference in New Issue
Block a user