diff options
author | Trond Myklebust <trond.myklebust@hammerspace.com> | 2023-08-09 04:17:11 +0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2023-08-30 17:11:06 +0300 |
commit | 96fb46ef8281c749abe114ed9385cec39bae00e4 (patch) | |
tree | 29e8e285158fce767b7e0bbdbc8e95c42bc78c6d /fs/nfs | |
parent | bdc544a87d43a4102aa08114fc939dbddf175f98 (diff) | |
download | linux-96fb46ef8281c749abe114ed9385cec39bae00e4.tar.xz |
NFS: Fix a use after free in nfs_direct_join_group()
commit be2fd1560eb57b7298aa3c258ddcca0d53ecdea3 upstream.
Be more careful when tearing down the subrequests of an O_DIRECT write
as part of a retransmission.
Reported-by: Chris Mason <clm@fb.com>
Fixes: ed5d588fe47f ("NFS: Try to join page groups before an O_DIRECT retransmission")
Cc: stable@vger.kernel.org
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/direct.c | 26 |
1 files changed, 16 insertions, 10 deletions
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 1707f46b1335..cf34d0c30945 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -474,20 +474,26 @@ out: return result; } -static void -nfs_direct_join_group(struct list_head *list, struct inode *inode) +static void nfs_direct_join_group(struct list_head *list, struct inode *inode) { - struct nfs_page *req, *next; + struct nfs_page *req, *subreq; list_for_each_entry(req, list, wb_list) { - if (req->wb_head != req || req->wb_this_page == req) + if (req->wb_head != req) continue; - for (next = req->wb_this_page; - next != req->wb_head; - next = next->wb_this_page) { - nfs_list_remove_request(next); - nfs_release_request(next); - } + subreq = req->wb_this_page; + if (subreq == req) + continue; + do { + /* + * Remove subrequests from this list before freeing + * them in the call to nfs_join_page_group(). + */ + if (!list_empty(&subreq->wb_list)) { + nfs_list_remove_request(subreq); + nfs_release_request(subreq); + } + } while ((subreq = subreq->wb_this_page) != req); nfs_join_page_group(req, inode); } } |