[CRIU] [PATCH 2/3] lazy-pages: make uffd_io_complete more robust

Mike Rapoport rppt at linux.vnet.ibm.com
Wed May 9 18:03:49 MSK 2018


Make sure we handle various corner cases:
* we received less pages than requested
* the request was capped because of unmap/remap etc
* the process has exited underneath us

Currently we are freeing the request once we've found the address to use
with uffd_copy(). Instead, let's keep the request object around, use it to
properly calculate number of pages we pass to uffd_copy() and then re-add
tailing range (if any) to the IOVs list.

Signed-off-by: Mike Rapoport <rppt at linux.vnet.ibm.com>
---
 criu/uffd.c | 30 ++++++++++++++++++++++--------
 1 file changed, 22 insertions(+), 8 deletions(-)

diff --git a/criu/uffd.c b/criu/uffd.c
index f019eee..f803f2a 100644
--- a/criu/uffd.c
+++ b/criu/uffd.c
@@ -845,7 +845,7 @@ static int uffd_io_complete(struct page_read *pr, unsigned long img_addr, int nr
 {
 	struct lazy_pages_info *lpi;
 	unsigned long addr = 0;
-	int req_pages;
+	int req_pages, ret;
 	struct lazy_iov *req;
 
 	lpi = container_of(pr, struct lazy_pages_info, pr);
@@ -861,25 +861,39 @@ static int uffd_io_complete(struct page_read *pr, unsigned long img_addr, int nr
 	list_for_each_entry(req, &lpi->reqs, l) {
 		if (req->img_start == img_addr) {
 			addr = req->start;
-			list_del(&req->l);
-			xfree(req);
 			break;
 		}
 	}
 
-	/* the request may be already gone because if uname/remove */
+	/* the request may be already gone because if unmap/remove */
 	if (!addr)
 		return 0;
 
 	/*
-	 * by the time we get the pages from the remote source, parts
-	 * of the request may already be gone because of
-	 * UFFD_EVENT_{REMAP,REMOVE,UNMAP}
+	 * By the time we get the pages from the remote source, parts
+	 * of the request may already be gone because of unmap/remove
+	 * OTOH, the remote side may send less pages than we requested.
+	 * Make sure we are not trying to uffd_copy more memory than
+	 * we should.
 	 */
 	req_pages = (req->end - req->start) / PAGE_SIZE;
 	nr = min(nr, req_pages);
 
-	return uffd_copy(lpi, addr, nr);
+	ret = uffd_copy(lpi, addr, nr);
+	if (ret < 0)
+		return ret;
+
+	/* recheck if the process exited, it may be detected in uffd_copy */
+	if (lpi->exited)
+		return 0;
+
+	/*
+	 * Since the completed request length may differ from the
+	 * actual data we've received we re-insert the request to IOVs
+	 * list and let drop_iovs do the range math, free memory etc.
+	 */
+	iov_list_insert(req, &lpi->iovs);
+	return drop_iovs(lpi, addr, nr * PAGE_SIZE);
 }
 
 static int uffd_zero(struct lazy_pages_info *lpi, __u64 address, int nr_pages)
-- 
2.7.4



More information about the CRIU mailing list