All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] ovl: fix dir cache leak for ovl_cache_get
@ 2017-05-04  7:07 Rock Lee
  2017-05-04  7:48 ` Amir Goldstein
  0 siblings, 1 reply; 6+ messages in thread
From: Rock Lee @ 2017-05-04  7:07 UTC (permalink / raw)
  To: miklos; +Cc: amir73il, linux-unionfs, Rock Lee

In ovl_cache_get, there is a chance that ovl_dentry_version_get and
cache->version are not equal. Before set the cache to NULL, free it
first.

Signed-off-by: Rock Lee <rockdotlee@gmail.com>
---
 fs/overlayfs/readdir.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
index f241b4e..b3af3d3 100644
--- a/fs/overlayfs/readdir.c
+++ b/fs/overlayfs/readdir.c
@@ -331,6 +331,8 @@ static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
 		cache->refcount++;
 		return cache;
 	}
+
+	kfree(cache);
 	ovl_set_dir_cache(dentry, NULL);
 
 	cache = kzalloc(sizeof(struct ovl_dir_cache), GFP_KERNEL);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] ovl: fix dir cache leak for ovl_cache_get
  2017-05-04  7:07 [PATCH] ovl: fix dir cache leak for ovl_cache_get Rock Lee
@ 2017-05-04  7:48 ` Amir Goldstein
  2017-05-05  6:34   ` Rock Lee
  0 siblings, 1 reply; 6+ messages in thread
From: Amir Goldstein @ 2017-05-04  7:48 UTC (permalink / raw)
  To: Rock Lee; +Cc: Miklos Szeredi, linux-unionfs

On Thu, May 4, 2017 at 10:07 AM, Rock Lee <rockdotlee@gmail.com> wrote:
> In ovl_cache_get, there is a chance that ovl_dentry_version_get and
> cache->version are not equal. Before set the cache to NULL, free it
> first.
>

This is not a leak. dir cache is refcounted and freed on
ovl_dir_{release|reset}() -> ovl_cache_put()

> Signed-off-by: Rock Lee <rockdotlee@gmail.com>
> ---
>  fs/overlayfs/readdir.c | 2 ++
>  1 file changed, 2 insertions(+)
>
> diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
> index f241b4e..b3af3d3 100644
> --- a/fs/overlayfs/readdir.c
> +++ b/fs/overlayfs/readdir.c
> @@ -331,6 +331,8 @@ static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
>                 cache->refcount++;
>                 return cache;
>         }
> +
> +       kfree(cache);
>         ovl_set_dir_cache(dentry, NULL);
>
>         cache = kzalloc(sizeof(struct ovl_dir_cache), GFP_KERNEL);
> --
> 1.9.1
>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] ovl: fix dir cache leak for ovl_cache_get
  2017-05-04  7:48 ` Amir Goldstein
@ 2017-05-05  6:34   ` Rock Lee
  2017-05-07 11:34     ` Amir Goldstein
  0 siblings, 1 reply; 6+ messages in thread
From: Rock Lee @ 2017-05-05  6:34 UTC (permalink / raw)
  To: Amir Goldstein; +Cc: Miklos Szeredi, linux-unionfs

Hi, Amir:

> This is not a leak. dir cache is refcounted and freed on
> ovl_dir_{release|reset}() -> ovl_cache_put()

Here is the snippet:

if (cache && ovl_dentry_version_get(dentry) == cache->version) {
cache->refcount++;
return cache;
}

ovl_set_dir_cache(dentry, NULL);

I am a little confused here. From the logic of the code, there must
have a circumstance --- cache is not NULL and
ovl_dentry_version_get(dentry) != cache->version. If
ovl_set_dir_cache(dentry, NULL), we can't find the old dir cache  form
dentry and struct file(later, file->private_data->cache will point at
the same dir cache with dentry). How could we free it in
ovl_dir_{release|reset} ?

BTW, could you please an example when ovl_dentry_version_get(dentry)
!= cache->version in ovl_cache_get ?

Thanks for your time.
-- 
Cheers,
Rock

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] ovl: fix dir cache leak for ovl_cache_get
  2017-05-05  6:34   ` Rock Lee
@ 2017-05-07 11:34     ` Amir Goldstein
  2017-05-08  9:54       ` Rock Lee
  0 siblings, 1 reply; 6+ messages in thread
From: Amir Goldstein @ 2017-05-07 11:34 UTC (permalink / raw)
  To: Rock Lee; +Cc: Miklos Szeredi, linux-unionfs

On Fri, May 5, 2017 at 9:34 AM, Rock Lee <rockdotlee@gmail.com> wrote:
> Hi, Amir:
>
>> This is not a leak. dir cache is refcounted and freed on
>> ovl_dir_{release|reset}() -> ovl_cache_put()
>
> Here is the snippet:
>
> if (cache && ovl_dentry_version_get(dentry) == cache->version) {
> cache->refcount++;
> return cache;
> }
>
> ovl_set_dir_cache(dentry, NULL);
>
> I am a little confused here. From the logic of the code, there must
> have a circumstance --- cache is not NULL and
> ovl_dentry_version_get(dentry) != cache->version. If
> ovl_set_dir_cache(dentry, NULL), we can't find the old dir cache  form
> dentry and struct file(later, file->private_data->cache will point at
> the same dir cache with dentry). How could we free it in
> ovl_dir_{release|reset} ?
>

It has a reference from another struct file, not this one
and cache will be freed when the last file that references it
is closed:

static int ovl_dir_release(struct inode *inode, struct file *file)
{
        struct ovl_dir_file *od = file->private_data;

        if (od->cache) {
                inode_lock(inode);
                ovl_cache_put(od, file->f_path.dentry);
                inode_unlock(inode);
        }

> BTW, could you please an example when ovl_dentry_version_get(dentry)
> != cache->version in ovl_cache_get ?
>

In theory (didn't try this code):

d1 = opendir("foo");
readdir(d1); /* populate d1's od->cache and read first entry */
pos = telldir(d1);
creat("foo/bar", 0600); /* ovl_dentry_version_inc() */
d2 = opendir("foo");
seekdir(d2, pos);
readdir(d2); /* populate d2's od->cache and read second entry */

d2's od->cache is NULL, ctx->pos = 1, and ovl_get_cache() will
find that ovl_dir_cache() is out dated and will create a new cache,
which should contain the new file "bar".

Amir.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] ovl: fix dir cache leak for ovl_cache_get
  2017-05-07 11:34     ` Amir Goldstein
@ 2017-05-08  9:54       ` Rock Lee
  2017-05-08 10:05         ` Amir Goldstein
  0 siblings, 1 reply; 6+ messages in thread
From: Rock Lee @ 2017-05-08  9:54 UTC (permalink / raw)
  To: Amir Goldstein; +Cc: Miklos Szeredi, linux-unionfs

On Sun, May 7, 2017 at 7:34 PM, Amir Goldstein <amir73il@gmail.com> wrote:
> On Fri, May 5, 2017 at 9:34 AM, Rock Lee <rockdotlee@gmail.com> wrote:
>> Hi, Amir:
>>
>>> This is not a leak. dir cache is refcounted and freed on
>>> ovl_dir_{release|reset}() -> ovl_cache_put()
>>
>> Here is the snippet:
>>
>> if (cache && ovl_dentry_version_get(dentry) == cache->version) {
>> cache->refcount++;
>> return cache;
>> }
>>
>> ovl_set_dir_cache(dentry, NULL);
>>
>> I am a little confused here. From the logic of the code, there must
>> have a circumstance --- cache is not NULL and
>> ovl_dentry_version_get(dentry) != cache->version. If
>> ovl_set_dir_cache(dentry, NULL), we can't find the old dir cache  form
>> dentry and struct file(later, file->private_data->cache will point at
>> the same dir cache with dentry). How could we free it in
>> ovl_dir_{release|reset} ?
>>
>
> It has a reference from another struct file, not this one
> and cache will be freed when the last file that references it
> is closed:
>
> static int ovl_dir_release(struct inode *inode, struct file *file)
> {
>         struct ovl_dir_file *od = file->private_data;
>
>         if (od->cache) {
>                 inode_lock(inode);
>                 ovl_cache_put(od, file->f_path.dentry);
>                 inode_unlock(inode);
>         }
>
>> BTW, could you please an example when ovl_dentry_version_get(dentry)
>> != cache->version in ovl_cache_get ?
>>
>
> In theory (didn't try this code):
>
> d1 = opendir("foo");
> readdir(d1); /* populate d1's od->cache and read first entry */
> pos = telldir(d1);
> creat("foo/bar", 0600); /* ovl_dentry_version_inc() */
> d2 = opendir("foo");
> seekdir(d2, pos);
> readdir(d2); /* populate d2's od->cache and read second entry */
>
> d2's od->cache is NULL, ctx->pos = 1, and ovl_get_cache() will
> find that ovl_dir_cache() is out dated and will create a new cache,
> which should contain the new file "bar".

Thanks for your explaination, I leaned a lot. I have tested your code,
and it met your expectation ;-).

One more question, only ctx->pos == 0 in ovl_iterate would cause
ovl_dir_reset and rebuild dir cache which means in some scenarios, dir
cache may be not up to date. Consider this code:

    DIR *dirp = opendir("foo");
    readdir(dirp);                /* make sure ctx->pos != 0 in the
blow readir() */
    creat("foo/bar" ,0600);  /* ovl_dentry_version_inc() */
    while ((entry = readdir(dirp)) != NULL) {
        printf("name %s\n", entry->d_name);
    }

In this case, "foo/bar" will not be shown in the output because
readdir will not rebuild dir cache. Could we just ovl_dir_reset()
everytime regardless of ctx->pos in ovl_iterate() ? Once files
created/removed/renamed in a directory, ovl_iterate will rebuild dir
cache and keep the dir cache up to date?

-- 
Cheers,
Rock

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] ovl: fix dir cache leak for ovl_cache_get
  2017-05-08  9:54       ` Rock Lee
@ 2017-05-08 10:05         ` Amir Goldstein
  0 siblings, 0 replies; 6+ messages in thread
From: Amir Goldstein @ 2017-05-08 10:05 UTC (permalink / raw)
  To: Rock Lee; +Cc: Miklos Szeredi, linux-unionfs

On Mon, May 8, 2017 at 12:54 PM, Rock Lee <rockdotlee@gmail.com> wrote:

>
> One more question, only ctx->pos == 0 in ovl_iterate would cause
> ovl_dir_reset and rebuild dir cache which means in some scenarios, dir
> cache may be not up to date. Consider this code:
>
>     DIR *dirp = opendir("foo");
>     readdir(dirp);                /* make sure ctx->pos != 0 in the
> blow readir() */
>     creat("foo/bar" ,0600);  /* ovl_dentry_version_inc() */
>     while ((entry = readdir(dirp)) != NULL) {
>         printf("name %s\n", entry->d_name);
>     }
>
> In this case, "foo/bar" will not be shown in the output because
> readdir will not rebuild dir cache. Could we just ovl_dir_reset()
> everytime regardless of ctx->pos in ovl_iterate() ? Once files
> created/removed/renamed in a directory, ovl_iterate will rebuild dir
> cache and keep the dir cache up to date?
>

We can't because:

1. After rebuilding the cache, you have no guaranty that ctx->pos is
pointing AFTER all the entries that you already read BEFORE rebuilding
the cache, so the result list can be a total disaster.

2. Even if we have a way to use persistent iterator position somehow,
rebuilding the cache on every iteration for a large dir is expensive.

Anyway, the situation you described - skipping an entry that was created
AFTER dir iteration has started can happen on any file system, so it's not
even a bug what you suggest to fix.

Amir.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2017-05-08 10:05 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-05-04  7:07 [PATCH] ovl: fix dir cache leak for ovl_cache_get Rock Lee
2017-05-04  7:48 ` Amir Goldstein
2017-05-05  6:34   ` Rock Lee
2017-05-07 11:34     ` Amir Goldstein
2017-05-08  9:54       ` Rock Lee
2017-05-08 10:05         ` Amir Goldstein

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.