2014-01-31 20:42:37

by Oleg Drokin

[permalink] [raw]
Subject: [PATCH] Fix mountpoint reference leakage in linkat

Recent changes to retry on ESTALE in linkat
(commit 442e31ca5a49e398351b2954b51f578353fdf210)
introduced a mountpoint reference leak and a small memory
leak in case a filesystem link operation returns ESTALE
which is pretty normal for distributed filesystems like
lustre, nfs and so on.
Free old_path in such a case.

Signed-off-by: Oleg Drokin: <[email protected]>
---
fs/namei.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/fs/namei.c b/fs/namei.c
index bcb838e..e620937 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3931,6 +3931,7 @@ out_dput:
goto retry;
}
if (retry_estale(error, how)) {
+ path_put(&old_path);
how |= LOOKUP_REVAL;
goto retry;
}
--
1.8.5.3


2014-01-31 20:52:59

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH] Fix mountpoint reference leakage in linkat

On Fri, 31 Jan 2014 15:41:58 -0500
Oleg Drokin <[email protected]> wrote:

> Recent changes to retry on ESTALE in linkat
> (commit 442e31ca5a49e398351b2954b51f578353fdf210)
> introduced a mountpoint reference leak and a small memory
> leak in case a filesystem link operation returns ESTALE
> which is pretty normal for distributed filesystems like
> lustre, nfs and so on.
> Free old_path in such a case.
>
> Signed-off-by: Oleg Drokin: <[email protected]>
> ---
> fs/namei.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/fs/namei.c b/fs/namei.c
> index bcb838e..e620937 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -3931,6 +3931,7 @@ out_dput:
> goto retry;
> }
> if (retry_estale(error, how)) {
> + path_put(&old_path);
> how |= LOOKUP_REVAL;
> goto retry;
> }

Nice catch. This should probably also go to stable as well...

Reviewed-by: Jeff Layton <[email protected]>

2014-01-31 21:03:37

by Al Viro

[permalink] [raw]
Subject: Re: [PATCH] Fix mountpoint reference leakage in linkat

On Fri, Jan 31, 2014 at 03:41:58PM -0500, Oleg Drokin wrote:
> Recent changes to retry on ESTALE in linkat
> (commit 442e31ca5a49e398351b2954b51f578353fdf210)
> introduced a mountpoint reference leak and a small memory
> leak in case a filesystem link operation returns ESTALE
> which is pretty normal for distributed filesystems like
> lustre, nfs and so on.
> Free old_path in such a case.
>
> Signed-off-by: Oleg Drokin: <[email protected]>
> ---
> fs/namei.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/fs/namei.c b/fs/namei.c
> index bcb838e..e620937 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -3931,6 +3931,7 @@ out_dput:
> goto retry;
> }
> if (retry_estale(error, how)) {
> + path_put(&old_path);
> how |= LOOKUP_REVAL;
> goto retry;
> }

Umm... That obviously can't be right - we have another goto retry
in the same situation (see in your diff context). I agree that
we have a leak there, but you've fixed only a half of it.

2014-01-31 21:13:29

by Oleg Drokin

[permalink] [raw]
Subject: Re: [PATCH] Fix mountpoint reference leakage in linkat


On Jan 31, 2014, at 4:03 PM, Al Viro wrote:
>> diff --git a/fs/namei.c b/fs/namei.c
>> index bcb838e..e620937 100644
>> --- a/fs/namei.c
>> +++ b/fs/namei.c
>> @@ -3931,6 +3931,7 @@ out_dput:
>> goto retry;
>> }
>> if (retry_estale(error, how)) {
>> + path_put(&old_path);
>> how |= LOOKUP_REVAL;
>> goto retry;
>> }
> Umm... That obviously can't be right - we have another goto retry
> in the same situation (see in your diff context). I agree that
> we have a leak there, but you've fixed only a half of it.

Hm, you are right, I did not notice this other one somehow.

So, not to take any guesses, should I convert the other goto retry into
retry_deleg similar in style to what happens in rename and unlink, only
make retry)deleg label before call to the security_path_link?
After the call to the security_path_link?
Or would you prefer to just free old_path in both cases?

Bye,
Oleg

2014-01-31 21:32:38

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH] Fix mountpoint reference leakage in linkat

On Fri, 31 Jan 2014 16:13:19 -0500
Oleg Drokin <[email protected]> wrote:

>
> On Jan 31, 2014, at 4:03 PM, Al Viro wrote:
> >> diff --git a/fs/namei.c b/fs/namei.c
> >> index bcb838e..e620937 100644
> >> --- a/fs/namei.c
> >> +++ b/fs/namei.c
> >> @@ -3931,6 +3931,7 @@ out_dput:
> >> goto retry;
> >> }
> >> if (retry_estale(error, how)) {
> >> + path_put(&old_path);
> >> how |= LOOKUP_REVAL;
> >> goto retry;
> >> }
> > Umm... That obviously can't be right - we have another goto retry
> > in the same situation (see in your diff context). I agree that
> > we have a leak there, but you've fixed only a half of it.
>
> Hm, you are right, I did not notice this other one somehow.
>
> So, not to take any guesses, should I convert the other goto retry into
> retry_deleg similar in style to what happens in rename and unlink, only
> make retry)deleg label before call to the security_path_link?
> After the call to the security_path_link?
> Or would you prefer to just free old_path in both cases?
>
> Bye,
> Oleg

Maybe something like this (untested) instead?

--------------------------8<---------------------------

[PATCH] vfs: fix linkat old_path reference leak

Cc: <[email protected]> # v3.8+
Reported-by: Oleg Drokin <[email protected]>
Signed-off-by: Jeff Layton <[email protected]>
---
fs/namei.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 46dbd31..e70dd81 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3927,8 +3927,10 @@ retry:
new_dentry = user_path_create(newdfd, newname, &new_path,
(how & LOOKUP_REVAL));
error = PTR_ERR(new_dentry);
- if (IS_ERR(new_dentry))
+ if (IS_ERR(new_dentry)) {
+ path_put(&old_path);
goto out;
+ }

error = -EXDEV;
if (old_path.mnt != new_path.mnt)
@@ -3942,6 +3944,7 @@ retry:
error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode);
out_dput:
done_path_create(&new_path, new_dentry);
+ path_put(&old_path);
if (delegated_inode) {
error = break_deleg_wait(&delegated_inode);
if (!error)
@@ -3952,8 +3955,6 @@ out_dput:
goto retry;
}
out:
- path_put(&old_path);
-
return error;
}

--
1.8.5.3



2014-01-31 21:50:41

by Oleg Drokin

[permalink] [raw]
Subject: Re: [PATCH] Fix mountpoint reference leakage in linkat

> Maybe something like this (untested) instead?

I think it's just safe to move the out label up along with the old_path freeing.

In all error cases the retry and delegation breaking logic could not be triggered
anyway.

Something like this (lightly tested):


Attachments:
0001-vfs-Fix-mountpoint-reference-leakage-in-linkat.patch (1.26 kB)

2014-01-31 22:30:23

by Al Viro

[permalink] [raw]
Subject: Re: [PATCH] Fix mountpoint reference leakage in linkat

On Fri, Jan 31, 2014 at 04:32:31PM -0500, Jeff Layton wrote:

> done_path_create(&new_path, new_dentry);
> + path_put(&old_path);

... and the filesystem in question isn't pinned anymore, so it can be
unmounted, except that

> if (delegated_inode) {
> error = break_deleg_wait(&delegated_inode);

this does an iput() on delegated_inode. And umount really doesn't
like finding pinned inodes. Sorry, no go.

I'm going with Oleg's patch with second path_put() added.

2014-01-31 22:34:32

by Oleg Drokin

[permalink] [raw]
Subject: [PATCH v2] vfs: Fix mountpoint reference leakage in linkat

Recent changes to retry on ESTALE in linkat
(commit 442e31ca5a49e398351b2954b51f578353fdf210)
introduced a mountpoint reference leak and a small memory
leak in case a filesystem link operation returns ESTALE
which is pretty normal for distributed filesystems like
lustre, nfs and so on.
Free old_path in such a case.
Also fix a similar leak in case of broken delegation.

Signed-off-by: Oleg Drokin: <[email protected]>
---
fs/namei.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/fs/namei.c b/fs/namei.c
index bcb838e..31ec503 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3927,10 +3927,13 @@ out_dput:
done_path_create(&new_path, new_dentry);
if (delegated_inode) {
error = break_deleg_wait(&delegated_inode);
- if (!error)
+ if (!error) {
+ path_put(&old_path);
goto retry;
+ }
}
if (retry_estale(error, how)) {
+ path_put(&old_path);
how |= LOOKUP_REVAL;
goto retry;
}
--
1.8.5.3

2014-01-31 23:11:38

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH] Fix mountpoint reference leakage in linkat

On Fri, 31 Jan 2014 22:30:14 +0000
Al Viro <[email protected]> wrote:

> On Fri, Jan 31, 2014 at 04:32:31PM -0500, Jeff Layton wrote:
>
> > done_path_create(&new_path, new_dentry);
> > + path_put(&old_path);
>
> ... and the filesystem in question isn't pinned anymore, so it can be
> unmounted, except that
>
> > if (delegated_inode) {
> > error = break_deleg_wait(&delegated_inode);
>
> this does an iput() on delegated_inode. And umount really doesn't
> like finding pinned inodes. Sorry, no go.
>
> I'm going with Oleg's patch with second path_put() added.

Good point. I hadn't considered that. Oleg's second patch looks good to
me too.

--
Jeff Layton <[email protected]>