Commit 42e32385 authored by Weston Andros Adamson's avatar Weston Andros Adamson Committed by Greg Kroah-Hartman

nfs: disallow duplicate pages in pgio page vectors

commit bba5c188 upstream.

Adjacent requests that share the same page are allowed, but should only
use one entry in the page vector. This avoids overruning the page
vector - it is sized based on how many bytes there are, not by
request count.

This fixes issues that manifest as "Redzone overwritten" bugs (the
vector overrun) and hangs waiting on page read / write, as it waits on
the same page more than once.

This also adds bounds checking to the page vector with a graceful failure
(WARN_ON_ONCE and pgio error returned to application).
Reported-by: default avatarToralf Förster <toralf.foerster@gmx.de>
Signed-off-by: default avatarWeston Andros Adamson <dros@primarydata.com>
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@primarydata.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 274a1dc9
...@@ -734,10 +734,11 @@ int nfs_generic_pgio(struct nfs_pageio_descriptor *desc, ...@@ -734,10 +734,11 @@ int nfs_generic_pgio(struct nfs_pageio_descriptor *desc,
struct nfs_pgio_header *hdr) struct nfs_pgio_header *hdr)
{ {
struct nfs_page *req; struct nfs_page *req;
struct page **pages; struct page **pages,
*last_page;
struct list_head *head = &desc->pg_list; struct list_head *head = &desc->pg_list;
struct nfs_commit_info cinfo; struct nfs_commit_info cinfo;
unsigned int pagecount; unsigned int pagecount, pageused;
pagecount = nfs_page_array_len(desc->pg_base, desc->pg_count); pagecount = nfs_page_array_len(desc->pg_base, desc->pg_count);
if (!nfs_pgarray_set(&hdr->page_array, pagecount)) if (!nfs_pgarray_set(&hdr->page_array, pagecount))
...@@ -745,12 +746,23 @@ int nfs_generic_pgio(struct nfs_pageio_descriptor *desc, ...@@ -745,12 +746,23 @@ int nfs_generic_pgio(struct nfs_pageio_descriptor *desc,
nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq); nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq);
pages = hdr->page_array.pagevec; pages = hdr->page_array.pagevec;
last_page = NULL;
pageused = 0;
while (!list_empty(head)) { while (!list_empty(head)) {
req = nfs_list_entry(head->next); req = nfs_list_entry(head->next);
nfs_list_remove_request(req); nfs_list_remove_request(req);
nfs_list_add_request(req, &hdr->pages); nfs_list_add_request(req, &hdr->pages);
*pages++ = req->wb_page;
if (WARN_ON_ONCE(pageused >= pagecount))
return nfs_pgio_error(desc, hdr);
if (!last_page || last_page != req->wb_page) {
*pages++ = last_page = req->wb_page;
pageused++;
}
} }
if (WARN_ON_ONCE(pageused != pagecount))
return nfs_pgio_error(desc, hdr);
if ((desc->pg_ioflags & FLUSH_COND_STABLE) && if ((desc->pg_ioflags & FLUSH_COND_STABLE) &&
(desc->pg_moreio || nfs_reqs_to_commit(&cinfo))) (desc->pg_moreio || nfs_reqs_to_commit(&cinfo)))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment