diff mbox series

cachefiles: fix dentry leak in cachefiles_open_file()

Message ID 20240826040018.2990763-1-libaokun@huaweicloud.com (mailing list archive)
State New
Headers show
Series cachefiles: fix dentry leak in cachefiles_open_file() | expand

Commit Message

Baokun Li Aug. 26, 2024, 4 a.m. UTC
From: Baokun Li <libaokun1@huawei.com>

In ondemand mode, dentry leaks may be caused when the mount has the
following concurrency with cull:

            P1             |             P2
-----------------------------------------------------------
cachefiles_lookup_cookie
  cachefiles_look_up_object
    lookup_one_positive_unlocked
     // get dentry
                            cachefiles_cull
                              inode->i_flags |= S_KERNEL_FILE;
    cachefiles_open_file
      cachefiles_mark_inode_in_use
        __cachefiles_mark_inode_in_use
          can_use = false
          if (!(inode->i_flags & S_KERNEL_FILE))
            can_use = true
	  return false
        return false
        // Returns an error but doesn't put dentry

After that the following WARNING will be triggered when the backend folder
is umounted:

==================================================================
BUG: Dentry 000000008ad87947{i=7a,n=Dx_1_1.img}  still in use (1) [unmount of ext4 sda]
WARNING: CPU: 4 PID: 359261 at fs/dcache.c:1767 umount_check+0x5d/0x70
CPU: 4 PID: 359261 Comm: umount Not tainted 6.6.0-dirty #25
RIP: 0010:umount_check+0x5d/0x70
Call Trace:
 <TASK>
 d_walk+0xda/0x2b0
 do_one_tree+0x20/0x40
 shrink_dcache_for_umount+0x2c/0x90
 generic_shutdown_super+0x20/0x160
 kill_block_super+0x1a/0x40
 ext4_kill_sb+0x22/0x40
 deactivate_locked_super+0x35/0x80
 cleanup_mnt+0x104/0x160
==================================================================

Add the missing dput() to cachefiles_open_file() for a quick fix.

Fixes: 1f08c925e7a3 ("cachefiles: Implement backing file wrangling")
Cc: stable@kernel.org
Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/cachefiles/namei.c | 1 +
 1 file changed, 1 insertion(+)

Comments

Markus Elfring Aug. 26, 2024, 1:55 p.m. UTC | #1
> Add the missing dput() to cachefiles_open_file() for a quick fix.

I suggest to use a goto chain accordingly.


…
> +++ b/fs/cachefiles/namei.c
> @@ -554,6 +554,7 @@ static bool cachefiles_open_file(struct cachefiles_object *object,
>  	if (!cachefiles_mark_inode_in_use(object, d_inode(dentry))) {
>  		pr_notice("cachefiles: Inode already in use: %pd (B=%lx)\n",
>  			  dentry, d_inode(dentry)->i_ino);
> +		dput(dentry);
>  		return false;

Please replace two statements by the statement “goto put_dentry;”.


…
> error:
> 	cachefiles_do_unmark_inode_in_use(object, d_inode(dentry));
+put_dentry:
> 	dput(dentry);
> 	return false;
> }

Regards,
Markus
Baokun Li Aug. 27, 2024, 3:47 a.m. UTC | #2
On 2024/8/26 21:55, Markus Elfring wrote:
> …
>> Add the missing dput() to cachefiles_open_file() for a quick fix.
> I suggest to use a goto chain accordingly.
>
>
> …

Hi Markus,


Thanks for the suggestion, but I think the current solution is simple
enough that we don't need to add a label to it.

Actually, at first I was going to release the reference count of the
dentry uniformly in cachefiles_look_up_object() and delete all dput()
in cachefiles_open_file(), but this may conflict when backporting
the code to stable. So just keep it simple to facilitate backporting
to stable.

Thanks,
Baokun
>> +++ b/fs/cachefiles/namei.c
>> @@ -554,6 +554,7 @@ static bool cachefiles_open_file(struct cachefiles_object *object,
>>   	if (!cachefiles_mark_inode_in_use(object, d_inode(dentry))) {
>>   		pr_notice("cachefiles: Inode already in use: %pd (B=%lx)\n",
>>   			  dentry, d_inode(dentry)->i_ino);
>> +		dput(dentry);
>>   		return false;
> Please replace two statements by the statement “goto put_dentry;”.
>
>
> …
>> error:
>> 	cachefiles_do_unmark_inode_in_use(object, d_inode(dentry));
> +put_dentry:
>> 	dput(dentry);
>> 	return false;
>> }
> Regards,
> Markus
Markus Elfring Aug. 27, 2024, 8:34 a.m. UTC | #3
>> …
>>> Add the missing dput() to cachefiles_open_file() for a quick fix.
>> I suggest to use a goto chain accordingly.> Thanks for the suggestion, but I think the current solution is simple enough

Yes, of course (in principle).


> that we don't need to add a label to it.

I came along other software design expectations.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/coding-style.rst?h=v6.11-rc5#n526

Regards,
Markus
David Howells Aug. 28, 2024, 1:01 p.m. UTC | #4
Baokun Li <libaokun@huaweicloud.com> wrote:

> Actually, at first I was going to release the reference count of the
> dentry uniformly in cachefiles_look_up_object() and delete all dput()
> in cachefiles_open_file(),

You couldn't do that anyway, since kernel_file_open() steals the caller's ref
if successful.

> but this may conflict when backporting the code to stable. So just keep it
> simple to facilitate backporting to stable.

Prioritise upstream, please.

I think Markus's suggestion of inserting a label and switching to a goto is
better.

Thanks,
David
Baokun Li Aug. 28, 2024, 2:05 p.m. UTC | #5
Hello David,

Thanks for the review.

On 2024/8/28 21:01, David Howells wrote:
> Baokun Li <libaokun@huaweicloud.com> wrote:
>
>> Actually, at first I was going to release the reference count of the
>> dentry uniformly in cachefiles_look_up_object() and delete all dput()
>> in cachefiles_open_file(),
> You couldn't do that anyway, since kernel_file_open() steals the caller's ref
> if successful.
Ignoring kernel_file_open(), we now put a reference count of the dentry
whether cachefiles_open_file() returns true or false.

And cachefiles_open_file() doesn't modify the dentry, so I'm thinking it's
releasing the reference count of the dentry that was got by
lookup_positive_unlocked() in cachefiles_look_up_object().

I'm not sure how kernel_file_open() steals the reference count,
am I missing something?


The code is as follows:

diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c
index f53977169db4..2b3f9935dbb4 100644
--- a/fs/cachefiles/namei.c
+++ b/fs/cachefiles/namei.c
@@ -595,14 +595,12 @@ static bool cachefiles_open_file(struct 
cachefiles_object *object,
          * write and readdir but not lookup or open).
          */
         touch_atime(&file->f_path);
-       dput(dentry);
         return true;

  check_failed:
         fscache_cookie_lookup_negative(object->cookie);
         cachefiles_unmark_inode_in_use(object, file);
         fput(file);
-       dput(dentry);
         if (ret == -ESTALE)
                 return cachefiles_create_file(object);
         return false;
@@ -611,7 +609,6 @@ static bool cachefiles_open_file(struct 
cachefiles_object *object,
         fput(file);
  error:
         cachefiles_do_unmark_inode_in_use(object, d_inode(dentry));
-       dput(dentry);
         return false;
  }

@@ -654,7 +651,9 @@ bool cachefiles_look_up_object(struct 
cachefiles_object *object)
                 goto new_file;
         }

-       if (!cachefiles_open_file(object, dentry))
+       ret = cachefiles_open_file(object, dentry);
+       dput(dentry);
+       if (!ret)
                 return false;

         _leave(" = t [%lu]", file_inode(object->file)->i_ino);


Regards,
Baokun

>> but this may conflict when backporting the code to stable. So just keep it
>> simple to facilitate backporting to stable.
> Prioritise upstream, please.
>
> I think Markus's suggestion of inserting a label and switching to a goto is
> better.
>
> Thanks,
> David
David Howells Aug. 28, 2024, 4:14 p.m. UTC | #6
Baokun Li <libaokun@huaweicloud.com> wrote:

> > You couldn't do that anyway, since kernel_file_open() steals the caller's ref
> > if successful.
> Ignoring kernel_file_open(), we now put a reference count of the dentry
> whether cachefiles_open_file() returns true or false.

Actually, I'm wrong kernel_file_open() doesn't steal a ref.

David
Baokun Li Aug. 29, 2024, 1:43 a.m. UTC | #7
Hi David,

On 2024/8/29 0:14, David Howells wrote:
> Baokun Li <libaokun@huaweicloud.com> wrote:
>
>>> You couldn't do that anyway, since kernel_file_open() steals the caller's ref
>>> if successful.
>> Ignoring kernel_file_open(), we now put a reference count of the dentry
>> whether cachefiles_open_file() returns true or false.
> Actually, I'm wrong kernel_file_open() doesn't steal a ref.
>
> David
>
>
Thanks for confirming this.
I will send a new version using the new solution.


Cheers,
Baokun
diff mbox series

Patch

diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c
index f53977169db4..0bd2f367c3ff 100644
--- a/fs/cachefiles/namei.c
+++ b/fs/cachefiles/namei.c
@@ -554,6 +554,7 @@  static bool cachefiles_open_file(struct cachefiles_object *object,
 	if (!cachefiles_mark_inode_in_use(object, d_inode(dentry))) {
 		pr_notice("cachefiles: Inode already in use: %pd (B=%lx)\n",
 			  dentry, d_inode(dentry)->i_ino);
+		dput(dentry);
 		return false;
 	}