2020-03-23 07:55:53

by Vasily Averin

[permalink] [raw]
Subject: [PATCH] nfsd: memory corruption in nfsd4_lock()

New struct nfsd4_blocked_lock allocated in find_or_allocate_block()
does not initialised nbl_list and nbl_lru.
If conflock allocation fails rollback can call list_del_init()
access uninitialized fields and corrupt memory.

Fixes: 76d348fadff5 ("nfsd: have nfsd4_lock use blocking locks for v4.1+ lock")
Signed-off-by: Vasily Averin <[email protected]>
---
fs/nfsd/nfs4state.c | 32 +++++++++++++++-----------------
1 file changed, 15 insertions(+), 17 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 369e574c5092..176ef8d24fae 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -6524,6 +6524,13 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
goto out;
}

+ conflock = locks_alloc_lock();
+ if (!conflock) {
+ dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
+ status = nfserr_jukebox;
+ goto out;
+ }
+
nbl = find_or_allocate_block(lock_sop, &fp->fi_fhandle, nn);
if (!nbl) {
dprintk("NFSD: %s: unable to allocate block!\n", __func__);
@@ -6542,13 +6549,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
file_lock->fl_end = last_byte_offset(lock->lk_offset, lock->lk_length);
nfs4_transform_lock_offset(file_lock);

- conflock = locks_alloc_lock();
- if (!conflock) {
- dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
- status = nfserr_jukebox;
- goto out;
- }
-
if (fl_flags & FL_SLEEP) {
nbl->nbl_time = jiffies;
spin_lock(&nn->blocked_locks_lock);
@@ -6581,17 +6581,15 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfserrno(err);
break;
}
-out:
- if (nbl) {
- /* dequeue it if we queued it before */
- if (fl_flags & FL_SLEEP) {
- spin_lock(&nn->blocked_locks_lock);
- list_del_init(&nbl->nbl_list);
- list_del_init(&nbl->nbl_lru);
- spin_unlock(&nn->blocked_locks_lock);
- }
- free_blocked_lock(nbl);
+ /* dequeue it if we queued it before */
+ if (fl_flags & FL_SLEEP) {
+ spin_lock(&nn->blocked_locks_lock);
+ list_del_init(&nbl->nbl_list);
+ list_del_init(&nbl->nbl_lru);
+ spin_unlock(&nn->blocked_locks_lock);
}
+ free_blocked_lock(nbl);
+out:
if (nf)
nfsd_file_put(nf);
if (lock_stp) {
--
2.17.1


2020-03-23 12:19:29

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH] nfsd: memory corruption in nfsd4_lock()

On Mon, 2020-03-23 at 10:55 +0300, Vasily Averin wrote:
> New struct nfsd4_blocked_lock allocated in find_or_allocate_block()
> does not initialised nbl_list and nbl_lru.
> If conflock allocation fails rollback can call list_del_init()
> access uninitialized fields and corrupt memory.
>
> Fixes: 76d348fadff5 ("nfsd: have nfsd4_lock use blocking locks for v4.1+ lock")
> Signed-off-by: Vasily Averin <[email protected]>
> ---
> fs/nfsd/nfs4state.c | 32 +++++++++++++++-----------------
> 1 file changed, 15 insertions(+), 17 deletions(-)
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index 369e574c5092..176ef8d24fae 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -6524,6 +6524,13 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> goto out;
> }
>
> + conflock = locks_alloc_lock();
> + if (!conflock) {
> + dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
> + status = nfserr_jukebox;
> + goto out;
> + }
> +
> nbl = find_or_allocate_block(lock_sop, &fp->fi_fhandle, nn);
> if (!nbl) {
> dprintk("NFSD: %s: unable to allocate block!\n", __func__);
> @@ -6542,13 +6549,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> file_lock->fl_end = last_byte_offset(lock->lk_offset, lock->lk_length);
> nfs4_transform_lock_offset(file_lock);
>
> - conflock = locks_alloc_lock();
> - if (!conflock) {
> - dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
> - status = nfserr_jukebox;
> - goto out;
> - }
> -
> if (fl_flags & FL_SLEEP) {
> nbl->nbl_time = jiffies;
> spin_lock(&nn->blocked_locks_lock);
> @@ -6581,17 +6581,15 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> status = nfserrno(err);
> break;
> }
> -out:
> - if (nbl) {
> - /* dequeue it if we queued it before */
> - if (fl_flags & FL_SLEEP) {
> - spin_lock(&nn->blocked_locks_lock);
> - list_del_init(&nbl->nbl_list);
> - list_del_init(&nbl->nbl_lru);
> - spin_unlock(&nn->blocked_locks_lock);
> - }
> - free_blocked_lock(nbl);
> + /* dequeue it if we queued it before */
> + if (fl_flags & FL_SLEEP) {
> + spin_lock(&nn->blocked_locks_lock);
> + list_del_init(&nbl->nbl_list);
> + list_del_init(&nbl->nbl_lru);
> + spin_unlock(&nn->blocked_locks_lock);
> }
> + free_blocked_lock(nbl);
> +out:
> if (nf)
> nfsd_file_put(nf);
> if (lock_stp) {

Good catch! Is there any reason not to just fix this by initializing the
list_heads in find_or_allocate_block? That seems like it'd be a simpler
fix.

--
Jeff Layton <[email protected]>

2020-03-23 13:51:47

by Chuck Lever III

[permalink] [raw]
Subject: Re: [PATCH] nfsd: memory corruption in nfsd4_lock()



> On Mar 23, 2020, at 3:55 AM, Vasily Averin <[email protected]> wrote:
>
> New struct nfsd4_blocked_lock allocated in find_or_allocate_block()
> does not initialised nbl_list and nbl_lru.
> If conflock allocation fails rollback can call list_del_init()
> access uninitialized fields and corrupt memory.
>
> Fixes: 76d348fadff5 ("nfsd: have nfsd4_lock use blocking locks for v4.1+ lock")
> Signed-off-by: Vasily Averin <[email protected]>
> ---
> fs/nfsd/nfs4state.c | 32 +++++++++++++++-----------------
> 1 file changed, 15 insertions(+), 17 deletions(-)
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index 369e574c5092..176ef8d24fae 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -6524,6 +6524,13 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> goto out;
> }
>
> + conflock = locks_alloc_lock();
> + if (!conflock) {
> + dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
> + status = nfserr_jukebox;
> + goto out;
> + }

Nit: What do people think about removing this dprintk() as part of the fix?


> nbl = find_or_allocate_block(lock_sop, &fp->fi_fhandle, nn);
> if (!nbl) {
> dprintk("NFSD: %s: unable to allocate block!\n", __func__);
> @@ -6542,13 +6549,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> file_lock->fl_end = last_byte_offset(lock->lk_offset, lock->lk_length);
> nfs4_transform_lock_offset(file_lock);
>
> - conflock = locks_alloc_lock();
> - if (!conflock) {
> - dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
> - status = nfserr_jukebox;
> - goto out;
> - }
> -
> if (fl_flags & FL_SLEEP) {
> nbl->nbl_time = jiffies;
> spin_lock(&nn->blocked_locks_lock);
> @@ -6581,17 +6581,15 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> status = nfserrno(err);
> break;
> }
> -out:
> - if (nbl) {
> - /* dequeue it if we queued it before */
> - if (fl_flags & FL_SLEEP) {
> - spin_lock(&nn->blocked_locks_lock);
> - list_del_init(&nbl->nbl_list);
> - list_del_init(&nbl->nbl_lru);
> - spin_unlock(&nn->blocked_locks_lock);
> - }
> - free_blocked_lock(nbl);
> + /* dequeue it if we queued it before */
> + if (fl_flags & FL_SLEEP) {
> + spin_lock(&nn->blocked_locks_lock);
> + list_del_init(&nbl->nbl_list);
> + list_del_init(&nbl->nbl_lru);
> + spin_unlock(&nn->blocked_locks_lock);
> }
> + free_blocked_lock(nbl);
> +out:
> if (nf)
> nfsd_file_put(nf);
> if (lock_stp) {
> --
> 2.17.1
>

--
Chuck Lever



2020-03-23 15:10:04

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH] nfsd: memory corruption in nfsd4_lock()

On Mon, Mar 23, 2020 at 09:50:34AM -0400, Chuck Lever wrote:
>
>
> > On Mar 23, 2020, at 3:55 AM, Vasily Averin <[email protected]> wrote:
> >
> > New struct nfsd4_blocked_lock allocated in find_or_allocate_block()
> > does not initialised nbl_list and nbl_lru.
> > If conflock allocation fails rollback can call list_del_init()
> > access uninitialized fields and corrupt memory.
> >
> > Fixes: 76d348fadff5 ("nfsd: have nfsd4_lock use blocking locks for v4.1+ lock")
> > Signed-off-by: Vasily Averin <[email protected]>
> > ---
> > fs/nfsd/nfs4state.c | 32 +++++++++++++++-----------------
> > 1 file changed, 15 insertions(+), 17 deletions(-)
> >
> > diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> > index 369e574c5092..176ef8d24fae 100644
> > --- a/fs/nfsd/nfs4state.c
> > +++ b/fs/nfsd/nfs4state.c
> > @@ -6524,6 +6524,13 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> > goto out;
> > }
> >
> > + conflock = locks_alloc_lock();
> > + if (!conflock) {
> > + dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
> > + status = nfserr_jukebox;
> > + goto out;
> > + }
>
> Nit: What do people think about removing this dprintk() as part of the fix?

I don't think we want a dprintk every place we kmalloc. All for
removing them.--b.

2020-03-23 22:13:12

by Vasily Averin

[permalink] [raw]
Subject: Re: [PATCH] nfsd: memory corruption in nfsd4_lock()

On 3/23/20 3:18 PM, Jeff Layton wrote:
> On Mon, 2020-03-23 at 10:55 +0300, Vasily Averin wrote:
>> New struct nfsd4_blocked_lock allocated in find_or_allocate_block()
>> does not initialised nbl_list and nbl_lru.
>> If conflock allocation fails rollback can call list_del_init()
>> access uninitialized fields and corrupt memory.
>>
>> Fixes: 76d348fadff5 ("nfsd: have nfsd4_lock use blocking locks for v4.1+ lock")
>> Signed-off-by: Vasily Averin <[email protected]>
>
> Good catch! Is there any reason not to just fix this by initializing the
> list_heads in find_or_allocate_block? That seems like it'd be a simpler
> fix.
>

Rollback in nfsd4_lock() is not optimal, I've tried to improve it too,
However I agree such improvement is not a simplest fix
and it anyway does not make whole rollback perfect.

I think it's better to re-send small fix for the found problem,
and prepare separate patches for rollback improvements,

Thank you,
Vasily Averin

2020-03-26 15:56:32

by Chuck Lever III

[permalink] [raw]
Subject: Re: [PATCH] nfsd: memory corruption in nfsd4_lock()

Howdy-

> On Mar 23, 2020, at 3:55 AM, Vasily Averin <[email protected]> wrote:
>
> New struct nfsd4_blocked_lock allocated in find_or_allocate_block()
> does not initialised nbl_list and nbl_lru.
> If conflock allocation fails rollback can call list_del_init()
> access uninitialized fields and corrupt memory.
>
> Fixes: 76d348fadff5 ("nfsd: have nfsd4_lock use blocking locks for v4.1+ lock")
> Signed-off-by: Vasily Averin <[email protected]>

I'm not certain where we stand with this one. Jeff, are you OK
with me taking this for v5.7, or is there additional work needed?

I can drop the dprintk when I merge it.


> ---
> fs/nfsd/nfs4state.c | 32 +++++++++++++++-----------------
> 1 file changed, 15 insertions(+), 17 deletions(-)
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index 369e574c5092..176ef8d24fae 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -6524,6 +6524,13 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> goto out;
> }
>
> + conflock = locks_alloc_lock();
> + if (!conflock) {
> + dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
> + status = nfserr_jukebox;
> + goto out;
> + }
> +
> nbl = find_or_allocate_block(lock_sop, &fp->fi_fhandle, nn);
> if (!nbl) {
> dprintk("NFSD: %s: unable to allocate block!\n", __func__);
> @@ -6542,13 +6549,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> file_lock->fl_end = last_byte_offset(lock->lk_offset, lock->lk_length);
> nfs4_transform_lock_offset(file_lock);
>
> - conflock = locks_alloc_lock();
> - if (!conflock) {
> - dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
> - status = nfserr_jukebox;
> - goto out;
> - }
> -
> if (fl_flags & FL_SLEEP) {
> nbl->nbl_time = jiffies;
> spin_lock(&nn->blocked_locks_lock);
> @@ -6581,17 +6581,15 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> status = nfserrno(err);
> break;
> }
> -out:
> - if (nbl) {
> - /* dequeue it if we queued it before */
> - if (fl_flags & FL_SLEEP) {
> - spin_lock(&nn->blocked_locks_lock);
> - list_del_init(&nbl->nbl_list);
> - list_del_init(&nbl->nbl_lru);
> - spin_unlock(&nn->blocked_locks_lock);
> - }
> - free_blocked_lock(nbl);
> + /* dequeue it if we queued it before */
> + if (fl_flags & FL_SLEEP) {
> + spin_lock(&nn->blocked_locks_lock);
> + list_del_init(&nbl->nbl_list);
> + list_del_init(&nbl->nbl_lru);
> + spin_unlock(&nn->blocked_locks_lock);
> }
> + free_blocked_lock(nbl);
> +out:
> if (nf)
> nfsd_file_put(nf);
> if (lock_stp) {
> --
> 2.17.1
>

--
Chuck Lever



2020-03-27 04:51:26

by Vasily Averin

[permalink] [raw]
Subject: [PATCH v2] nfsd: memory corruption in nfsd4_lock()

Dear Chuck,
please use following patch instead.
-----
New struct nfsd4_blocked_lock allocated in find_or_allocate_block()
does not initialized nbl_list and nbl_lru.
If conflock allocation fails rollback can call list_del_init()
access uninitialized fields and corrupt memory.

v2: just initialize nbl_list and nbl_lru right after nbl allocation.

Fixes: 76d348fadff5 ("nfsd: have nfsd4_lock use blocking locks for v4.1+ lock")
Signed-off-by: Vasily Averin <[email protected]>
---
fs/nfsd/nfs4state.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 369e574c5092..1b2eb6b35d64 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -266,6 +266,8 @@ find_or_allocate_block(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
if (!nbl) {
nbl= kmalloc(sizeof(*nbl), GFP_KERNEL);
if (nbl) {
+ INIT_LIST_HEAD(&nbl->nbl_list);
+ INIT_LIST_HEAD(&nbl->nbl_lru);
fh_copy_shallow(&nbl->nbl_fh, fh);
locks_init_lock(&nbl->nbl_lock);
nfsd4_init_cb(&nbl->nbl_cb, lo->lo_owner.so_client,
--
2.17.1

2020-03-30 10:24:11

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH v2] nfsd: memory corruption in nfsd4_lock()

On Fri, 2020-03-27 at 07:50 +0300, Vasily Averin wrote:
> Dear Chuck,
> please use following patch instead.
> -----
> New struct nfsd4_blocked_lock allocated in find_or_allocate_block()
> does not initialized nbl_list and nbl_lru.
> If conflock allocation fails rollback can call list_del_init()
> access uninitialized fields and corrupt memory.
>
> v2: just initialize nbl_list and nbl_lru right after nbl allocation.
>
> Fixes: 76d348fadff5 ("nfsd: have nfsd4_lock use blocking locks for v4.1+ lock")
> Signed-off-by: Vasily Averin <[email protected]>
> ---
> fs/nfsd/nfs4state.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index 369e574c5092..1b2eb6b35d64 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -266,6 +266,8 @@ find_or_allocate_block(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
> if (!nbl) {
> nbl= kmalloc(sizeof(*nbl), GFP_KERNEL);
> if (nbl) {
> + INIT_LIST_HEAD(&nbl->nbl_list);
> + INIT_LIST_HEAD(&nbl->nbl_lru);
> fh_copy_shallow(&nbl->nbl_fh, fh);
> locks_init_lock(&nbl->nbl_lock);
> nfsd4_init_cb(&nbl->nbl_cb, lo->lo_owner.so_client,

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

2020-03-30 14:54:30

by Chuck Lever III

[permalink] [raw]
Subject: Re: [PATCH v2] nfsd: memory corruption in nfsd4_lock()



> On Mar 30, 2020, at 6:22 AM, Jeff Layton <[email protected]> wrote:
>
> On Fri, 2020-03-27 at 07:50 +0300, Vasily Averin wrote:
>> Dear Chuck,
>> please use following patch instead.

Somehow this did not make it to my inbox on Friday, but Jeff's
Reviewed-by did show up today. I'll apply this one, thanks!


>> -----
>> New struct nfsd4_blocked_lock allocated in find_or_allocate_block()
>> does not initialized nbl_list and nbl_lru.
>> If conflock allocation fails rollback can call list_del_init()
>> access uninitialized fields and corrupt memory.
>>
>> v2: just initialize nbl_list and nbl_lru right after nbl allocation.
>>
>> Fixes: 76d348fadff5 ("nfsd: have nfsd4_lock use blocking locks for v4.1+ lock")
>> Signed-off-by: Vasily Averin <[email protected]>
>> ---
>> fs/nfsd/nfs4state.c | 2 ++
>> 1 file changed, 2 insertions(+)
>>
>> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
>> index 369e574c5092..1b2eb6b35d64 100644
>> --- a/fs/nfsd/nfs4state.c
>> +++ b/fs/nfsd/nfs4state.c
>> @@ -266,6 +266,8 @@ find_or_allocate_block(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
>> if (!nbl) {
>> nbl= kmalloc(sizeof(*nbl), GFP_KERNEL);
>> if (nbl) {
>> + INIT_LIST_HEAD(&nbl->nbl_list);
>> + INIT_LIST_HEAD(&nbl->nbl_lru);
>> fh_copy_shallow(&nbl->nbl_fh, fh);
>> locks_init_lock(&nbl->nbl_lock);
>> nfsd4_init_cb(&nbl->nbl_cb, lo->lo_owner.so_client,
>
> Reviewed-by: Jeff Layton <[email protected]>

--
Chuck Lever