| From 4b310319c6a8ce708f1033d57145e2aa027a883c Mon Sep 17 00:00:00 2001 |
| From: Trond Myklebust <trondmy@gmail.com> |
| Date: Sun, 2 Feb 2020 17:53:53 -0500 |
| Subject: NFS: Fix memory leaks and corruption in readdir |
| |
| From: Trond Myklebust <trondmy@gmail.com> |
| |
| commit 4b310319c6a8ce708f1033d57145e2aa027a883c upstream. |
| |
| nfs_readdir_xdr_to_array() must not exit without having initialised |
| the array, so that the page cache deletion routines can safely |
| call nfs_readdir_clear_array(). |
| Furthermore, we should ensure that if we exit nfs_readdir_filler() |
| with an error, we free up any page contents to prevent a leak |
| if we try to fill the page again. |
| |
| Fixes: 11de3b11e08c ("NFS: Fix a memory leak in nfs_readdir") |
| Cc: stable@vger.kernel.org # v2.6.37+ |
| Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> |
| Reviewed-by: Benjamin Coddington <bcodding@redhat.com> |
| Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/nfs/dir.c | 17 +++++++++++++++-- |
| 1 file changed, 15 insertions(+), 2 deletions(-) |
| |
| --- a/fs/nfs/dir.c |
| +++ b/fs/nfs/dir.c |
| @@ -162,6 +162,17 @@ typedef struct { |
| bool eof; |
| } nfs_readdir_descriptor_t; |
| |
| +static |
| +void nfs_readdir_init_array(struct page *page) |
| +{ |
| + struct nfs_cache_array *array; |
| + |
| + array = kmap_atomic(page); |
| + memset(array, 0, sizeof(struct nfs_cache_array)); |
| + array->eof_index = -1; |
| + kunmap_atomic(array); |
| +} |
| + |
| /* |
| * we are freeing strings created by nfs_add_to_readdir_array() |
| */ |
| @@ -174,6 +185,7 @@ void nfs_readdir_clear_array(struct page |
| array = kmap_atomic(page); |
| for (i = 0; i < array->size; i++) |
| kfree(array->array[i].string.name); |
| + array->size = 0; |
| kunmap_atomic(array); |
| } |
| |
| @@ -610,6 +622,8 @@ int nfs_readdir_xdr_to_array(nfs_readdir |
| int status = -ENOMEM; |
| unsigned int array_size = ARRAY_SIZE(pages); |
| |
| + nfs_readdir_init_array(page); |
| + |
| entry.prev_cookie = 0; |
| entry.cookie = desc->last_cookie; |
| entry.eof = 0; |
| @@ -626,8 +640,6 @@ int nfs_readdir_xdr_to_array(nfs_readdir |
| } |
| |
| array = kmap(page); |
| - memset(array, 0, sizeof(struct nfs_cache_array)); |
| - array->eof_index = -1; |
| |
| status = nfs_readdir_alloc_pages(pages, array_size); |
| if (status < 0) |
| @@ -681,6 +693,7 @@ int nfs_readdir_filler(nfs_readdir_descr |
| unlock_page(page); |
| return 0; |
| error: |
| + nfs_readdir_clear_array(page); |
| unlock_page(page); |
| return ret; |
| } |