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:
Bernd Schubert
2026-06-17 11:51:46 +02:00
parent 9fbe6a0f17
commit 8aa8ba677d
+10 -5
View File
@@ -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;