public inbox for linux-fsdevel@vger.kernel.org
 help / color / mirror / Atom feed
From: Luis Henriques <luis@igalia.com>
To: Miklos Szeredi <mszeredi@redhat.com>
Cc: linux-fsdevel@vger.kernel.org,
	 Alexander Viro <viro@zeniv.linux.org.uk>,
	 Christian Brauner <brauner@kernel.org>,
	 Bernd Schubert <bernd.schubert@fastmail.fm>,
	 Laura Promberger <laura.promberger@cern.ch>,
	 Sam Lewis <samclewis@google.com>
Subject: Re: [PATCH] fuse: don't truncate cached, mutated symlink
Date: Fri, 21 Feb 2025 10:38:56 +0000	[thread overview]
Message-ID: <87y0xzh9a7.fsf@igalia.com> (raw)
In-Reply-To: <20250220100258.793363-1-mszeredi@redhat.com> (Miklos Szeredi's message of "Thu, 20 Feb 2025 11:02:58 +0100")

On Thu, Feb 20 2025, Miklos Szeredi wrote:

> 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 <laura.promberger@cern.ch>
> Tested-by: Sam Lewis <samclewis@google.com>
> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>

OK, I finally managed to reproduce the bug (thanks for the hints, Sam!)
and I can also confirm this patch fixes it.

So, feel free to add my

Reviewed-by: Luis Henriques <luis@igalia.com>
Tested-by: Luis Henriques <luis@igalia.com>

Cheers,
-- 
Luís

> ---
>  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 589e88822368..83c56ce6ad20 100644
> --- a/fs/fuse/dir.c
> +++ b/fs/fuse/dir.c
> @@ -1645,7 +1645,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 3ab9440c5b93..ecb7b95c2ca3 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -5356,10 +5356,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;
>  
> @@ -5378,8 +5377,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 2c3b2f8a621f..9346adf28f7b 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -3452,6 +3452,8 @@ extern const struct file_operations generic_ro_fops;
>  
>  extern int readlink_copy(char __user *, int, const char *, int);
>  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.48.1
>
>


  parent reply	other threads:[~2025-02-21 10:39 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-02-20 10:02 [PATCH] fuse: don't truncate cached, mutated symlink Miklos Szeredi
2025-02-20 10:26 ` Bernd Schubert
2025-02-20 14:48 ` Christian Brauner
2025-02-21 10:38 ` Luis Henriques [this message]
2025-02-23  0:28 ` Al Viro
2025-02-23 19:12   ` Miklos Szeredi
2025-02-23 19:41     ` Al Viro
2025-02-25 13:01       ` Laura Promberger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87y0xzh9a7.fsf@igalia.com \
    --to=luis@igalia.com \
    --cc=bernd.schubert@fastmail.fm \
    --cc=brauner@kernel.org \
    --cc=laura.promberger@cern.ch \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=mszeredi@redhat.com \
    --cc=samclewis@google.com \
    --cc=viro@zeniv.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox