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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox