From: Henrique Carvalho <henrique.carvalho@suse.com>
To: Paul Aurich <paul@darkrain42.org>
Cc: ematsumiya@suse.de, sfrench@samba.org, smfrench@gmail.com,
pc@manguebit.com, ronniesahlberg@gmail.com,
sprasad@microsoft.com, bharathsm@microsoft.com,
linux-cifs@vger.kernel.org
Subject: Re: [PATCH] smb: client: avoid dentry leak by not overwriting cfid->dentry
Date: Wed, 7 May 2025 11:03:26 -0300 [thread overview]
Message-ID: <aBtoLiUiLhAS89-W@precision> (raw)
In-Reply-To: <aBr6zohhW9Akuu3a@redcloak.home.arpa>
On Tue, May 06, 2025 at 11:16:46PM -0700, Paul Aurich wrote:
> On 2025-05-06 19:31:56 -0300, Henrique Carvalho wrote:
> > A race, likely between lease break and open, can cause cfid->dentry to
> > be valid when open_cached_dir() tries to set it again. This overwrites
> > the old dentry without dput(), leaking it.
> >
> > Skip assignment if cfid->dentry is already set.
> >
> > Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
> > ---
> > fs/smb/client/cached_dir.c | 23 +++++++++++++++--------
> > 1 file changed, 15 insertions(+), 8 deletions(-)
> >
> > diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c
> > index 43228ec2424d..8c1f00a3fc29 100644
> > --- a/fs/smb/client/cached_dir.c
> > +++ b/fs/smb/client/cached_dir.c
> > @@ -219,16 +219,23 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
> > goto out;
> > }
> >
> > - if (!npath[0]) {
> > - dentry = dget(cifs_sb->root);
> > - } else {
> > - dentry = path_to_dentry(cifs_sb, npath);
> > - if (IS_ERR(dentry)) {
> > - rc = -ENOENT;
> > - goto out;
> > + /*
> > + * BB: cfid->dentry should be NULL here; if not, we're likely racing with
> > + * a lease break. This is a temporary workaround to avoid overwriting
> > + * a valid dentry. Needs proper fix.
> > + */
>
> Ah ha. I think this is trying to address the same race as Shyam's 'cifs: do
> not return an invalidated cfid' [1].
>
> What about modifying open_cached_dir to hold cfid_list_lock across the call
> to find_or_create_cached_dir through where it tests for validity, and then
> dropping the locking in find_or_create_cached_dir itself (see attached in
> case my text description isn't clear)?
>
> That's the only way I can see that a pre-existing cfid could escape to the
> rest of open_cached_dir. I think.
>
> ~Paul
>
> [1] https://lore.kernel.org/linux-cifs/20250502051517.10449-2-sprasad@microsoft.com/T/#u
>
> > + if (!cfid->dentry) {
> > + if (!npath[0]) {
> > + dentry = dget(cifs_sb->root);
> > + } else {
> > + dentry = path_to_dentry(cifs_sb, npath);
> > + if (IS_ERR(dentry)) {
> > + rc = -ENOENT;
> > + goto out;
> > + }
> > }
> > + cfid->dentry = dentry;
> > }
> > - cfid->dentry = dentry;
> > cfid->tcon = tcon;
> >
> > /*
> > --
> > 2.47.0
> >From 583824153dd901f1f7d63ce9364d32c9c249fd43 Mon Sep 17 00:00:00 2001
> From: Paul Aurich <paul@darkrain42.org>
> Date: Tue, 6 May 2025 22:28:09 -0700
> Subject: [PATCH] smb: client: Avoid race in open_cached_dir with lease breaks
>
> A pre-existing valid cfid returned from find_or_create_cached_dir might
> race with a lease break, meaning open_cached_dir doesn't consider it
> valid, and thinks it's newly-constructed. This leaks a dentry reference
> if the allocation occurs before the queued lease break work runs.
>
> Avoid the race by extending holding the cfid_list_lock across
> find_or_create_cached_dir and when the result is checked.
>
> Signed-off-by: Paul Aurich <paul@darkrain42.org>
> Cc: stable@vger.kernel.org
> ---
> fs/smb/client/cached_dir.c | 10 ++--------
> 1 file changed, 2 insertions(+), 8 deletions(-)
>
> diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c
> index 10aad87ce679..cb8477c18905 100644
> --- a/fs/smb/client/cached_dir.c
> +++ b/fs/smb/client/cached_dir.c
> @@ -27,38 +27,32 @@ static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids,
> bool lookup_only,
> __u32 max_cached_dirs)
> {
> struct cached_fid *cfid;
>
> - spin_lock(&cfids->cfid_list_lock);
> list_for_each_entry(cfid, &cfids->entries, entry) {
> if (!strcmp(cfid->path, path)) {
> /*
> * If it doesn't have a lease it is either not yet
> * fully cached or it may be in the process of
> * being deleted due to a lease break.
> */
> if (!cfid->time || !cfid->has_lease) {
> - spin_unlock(&cfids->cfid_list_lock);
> return NULL;
> }
> kref_get(&cfid->refcount);
> - spin_unlock(&cfids->cfid_list_lock);
> return cfid;
> }
> }
> if (lookup_only) {
> - spin_unlock(&cfids->cfid_list_lock);
> return NULL;
> }
> if (cfids->num_entries >= max_cached_dirs) {
> - spin_unlock(&cfids->cfid_list_lock);
> return NULL;
> }
> cfid = init_cached_dir(path);
> if (cfid == NULL) {
> - spin_unlock(&cfids->cfid_list_lock);
> return NULL;
> }
> cfid->cfids = cfids;
> cfids->num_entries++;
> list_add(&cfid->entry, &cfids->entries);
> @@ -72,11 +66,10 @@ static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids,
> * Concurrent processes won't be to use it yet due to @cfid->time being
> * zero.
> */
> cfid->has_lease = true;
>
> - spin_unlock(&cfids->cfid_list_lock);
> return cfid;
> }
>
> static struct dentry *
> path_to_dentry(struct cifs_sb_info *cifs_sb, const char *path)
> @@ -185,21 +178,22 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
>
> utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
> if (!utf16_path)
> return -ENOMEM;
>
> + spin_lock(&cfids->cfid_list_lock);
> cfid = find_or_create_cached_dir(cfids, path, lookup_only, tcon->max_cached_dirs);
> if (cfid == NULL) {
> + spin_unlock(&cfids->cfid_list_lock);
> kfree(utf16_path);
> return -ENOENT;
> }
> /*
> * Return cached fid if it is valid (has a lease and has a time).
> * Otherwise, it is either a new entry or laundromat worker removed it
> * from @cfids->entries. Caller will put last reference if the latter.
> */
> - spin_lock(&cfids->cfid_list_lock);
> if (cfid->has_lease && cfid->time) {
> spin_unlock(&cfids->cfid_list_lock);
> *ret_cfid = cfid;
> kfree(utf16_path);
> return 0;
> --
> 2.47.2
>
Yes!
Closing this gap seems to be the only viable fix.
I'm running the tests, but LGTM.
Best,
Henrique
next prev parent reply other threads:[~2025-05-07 14:05 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-05-06 22:31 [PATCH] smb: client: avoid dentry leak by not overwriting cfid->dentry Henrique Carvalho
2025-05-07 6:16 ` Paul Aurich
2025-05-07 14:03 ` Henrique Carvalho [this message]
[not found] ` <CAH2r5muyz7zY=+Fgrtc_zOA6GR1ZSGpR-Z4pFzgqmfszhnywWQ@mail.gmail.com>
2025-05-07 17:01 ` Fwd: " Steve French
[not found] ` <CAH2r5msisxGqZCFpJUu1Bqv5Kgo+-HD2DEO+wzQeSqG6TS2J6Q@mail.gmail.com>
2025-05-07 19:38 ` Henrique Carvalho
2025-05-07 23:56 ` Steve French
2025-05-08 15:24 ` Shyam Prasad N
2025-05-08 15:53 ` Paul Aurich
2025-05-08 16:09 ` Steve French
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=aBtoLiUiLhAS89-W@precision \
--to=henrique.carvalho@suse.com \
--cc=bharathsm@microsoft.com \
--cc=ematsumiya@suse.de \
--cc=linux-cifs@vger.kernel.org \
--cc=paul@darkrain42.org \
--cc=pc@manguebit.com \
--cc=ronniesahlberg@gmail.com \
--cc=sfrench@samba.org \
--cc=smfrench@gmail.com \
--cc=sprasad@microsoft.com \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.