From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1794E2638B5; Mon, 3 Mar 2025 16:31:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741019503; cv=none; b=fHtRnmK9aHxn03XHRhoS2qEbD7xKFqoAmVhmkCLWeUWKL2EQbkBGNtUfSsIMQmPzEHc6QInDxN4qF+rmSEZ9pNkI72DwroI9PkSUDv2KB6x2q3HMQFOF3b78sIAUBFwFpDm5L1/dMxPbqluOD6XGTW1Ymrb59fwiQRN8bEHygm8= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741019503; c=relaxed/simple; bh=YkB+E3Q0eljYQymts/mr+hzK5f5pNYgPM7cDoGEGgIQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=aKnH9zAA4sh4i8Kk+EZvil2D/NLIICWD9kWAACIVbFeOtCJXPVq9LuG+4HN9tbNInTb0f19yCDVosVm0gsJ45YqbIPxoQ1nA8iLLMaUerBSa2DiLAK+hKCCff3jQXP9EZ0JU84/Mrr0DuLWnYcpQHOnzwkMa5ppvEfxr/FX7jgs= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Ezydahbp; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Ezydahbp" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 49B30C4CEE9; Mon, 3 Mar 2025 16:31:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1741019502; bh=YkB+E3Q0eljYQymts/mr+hzK5f5pNYgPM7cDoGEGgIQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=EzydahbpxjE7zfPuhyOoiO7sOFE+SJzrB4H5yFyefg5pY4ED6+mG4hQ0DLv3/j9jl 0Z2LYso1/GYhM8h/tilHpm7MO/bBYfPSMCp/NhQSDQTSyxOduYFPZRhxW4JqeuIJRE mGJCf6ZEyrNZJj9cDfg3LhmIKjfyFlj43RtASXS6yOS3cMAs+sl5X6yvQXgkIGEFli 3jgdkhebOegb/lZcx19N8iYmepN5DazACy/FUcm3IXJDf7oPDh2uDUYeHBwIz0j9Me 5ey3ax7Mny3LlsZdIJffaJGj9SGjpx66o/mped7o3qPSnS9tb6y7jmo2/6mmdUQO4t 5ZC9Xgmcw5Jjg== From: Sasha Levin To: linux-kernel@vger.kernel.org, stable@vger.kernel.org Cc: Miklos Szeredi , Laura Promberger , Sam Lewis , Bernd Schubert , Christian Brauner , Sasha Levin , miklos@szeredi.hu, viro@zeniv.linux.org.uk, linux-fsdevel@vger.kernel.org Subject: [PATCH AUTOSEL 6.1 4/9] fuse: don't truncate cached, mutated symlink Date: Mon, 3 Mar 2025 11:31:28 -0500 Message-Id: <20250303163133.3764032-4-sashal@kernel.org> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250303163133.3764032-1-sashal@kernel.org> References: <20250303163133.3764032-1-sashal@kernel.org> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-stable: review X-Patchwork-Hint: Ignore X-stable-base: Linux 6.1.129 Content-Transfer-Encoding: 8bit From: Miklos Szeredi [ Upstream commit b4c173dfbb6c78568578ff18f9e8822d7bd0e31b ] Fuse allows the value of a symlink to change and this property is exploited by some filesystems (e.g. CVMFS). It has been observed, that sometimes after changing the symlink contents, the value is truncated to the old size. This is caused by fuse_getattr() racing with fuse_reverse_inval_inode(). fuse_reverse_inval_inode() updates the fuse_inode's attr_version, which results in fuse_change_attributes() exiting before updating the cached attributes This is okay, as the cached attributes remain invalid and the next call to fuse_change_attributes() will likely update the inode with the correct values. The reason this causes problems is that cached symlinks will be returned through page_get_link(), which truncates the symlink to inode->i_size. This is correct for filesystems that don't mutate symlinks, but in this case it causes bad behavior. The solution is to just remove this truncation. This can cause a regression in a filesystem that relies on supplying a symlink larger than the file size, but this is unlikely. If that happens we'd need to make this behavior conditional. Reported-by: Laura Promberger Tested-by: Sam Lewis Signed-off-by: Miklos Szeredi Link: https://lore.kernel.org/r/20250220100258.793363-1-mszeredi@redhat.com Reviewed-by: Bernd Schubert Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/fuse/dir.c | 2 +- fs/namei.c | 24 +++++++++++++++++++----- include/linux/fs.h | 2 ++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index aa2be4c1ea8f2..de31cb8eb7201 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1445,7 +1445,7 @@ static const char *fuse_get_link(struct dentry *dentry, struct inode *inode, goto out_err; if (fc->cache_symlinks) - return page_get_link(dentry, inode, callback); + return page_get_link_raw(dentry, inode, callback); err = -ECHILD; if (!dentry) diff --git a/fs/namei.c b/fs/namei.c index 166d71c82d7ac..6ce07cde1c277 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -5156,10 +5156,9 @@ const char *vfs_get_link(struct dentry *dentry, struct delayed_call *done) EXPORT_SYMBOL(vfs_get_link); /* get the link contents into pagecache */ -const char *page_get_link(struct dentry *dentry, struct inode *inode, - struct delayed_call *callback) +static char *__page_get_link(struct dentry *dentry, struct inode *inode, + struct delayed_call *callback) { - char *kaddr; struct page *page; struct address_space *mapping = inode->i_mapping; @@ -5178,8 +5177,23 @@ const char *page_get_link(struct dentry *dentry, struct inode *inode, } set_delayed_call(callback, page_put_link, page); BUG_ON(mapping_gfp_mask(mapping) & __GFP_HIGHMEM); - kaddr = page_address(page); - nd_terminate_link(kaddr, inode->i_size, PAGE_SIZE - 1); + return page_address(page); +} + +const char *page_get_link_raw(struct dentry *dentry, struct inode *inode, + struct delayed_call *callback) +{ + return __page_get_link(dentry, inode, callback); +} +EXPORT_SYMBOL_GPL(page_get_link_raw); + +const char *page_get_link(struct dentry *dentry, struct inode *inode, + struct delayed_call *callback) +{ + char *kaddr = __page_get_link(dentry, inode, callback); + + if (!IS_ERR(kaddr)) + nd_terminate_link(kaddr, inode->i_size, PAGE_SIZE - 1); return kaddr; } diff --git a/include/linux/fs.h b/include/linux/fs.h index 0d32634c5cf0d..08fba309ddc78 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -3385,6 +3385,8 @@ extern const struct file_operations generic_ro_fops; extern int readlink_copy(char __user *, int, const char *); extern int page_readlink(struct dentry *, char __user *, int); +extern const char *page_get_link_raw(struct dentry *, struct inode *, + struct delayed_call *); extern const char *page_get_link(struct dentry *, struct inode *, struct delayed_call *); extern void page_put_link(void *); -- 2.39.5