[CRIU] [PATCH 2/2] mem: Don't mprotect VMA areas if there is no page to dump

Cyrill Gorcunov gorcunov at openvz.org
Thu Jan 30 04:47:34 PST 2014


We've discovered a kernel bug -- mprotect called on page
with soft-dirty bit set might drop it once mprotect complete.
The kernel fix already on the way but we decided to workaround
this problem in criu as well: if there is no page to be dumped
we simply don't call mprotect on such vma.

Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---
 mem.c | 90 ++++++++++++++++++++++++++++++++++---------------------------------
 1 file changed, 46 insertions(+), 44 deletions(-)

diff --git a/mem.c b/mem.c
index b00d03a7f2ee..2e55a1dc6b83 100644
--- a/mem.c
+++ b/mem.c
@@ -101,7 +101,9 @@ static inline bool page_in_parent(u64 pme)
  * the memory contents is present in the pagent image set.
  */
 
-static int generate_iovs(struct vma_area *vma, int pagemap, struct page_pipe *pp, u64 *map)
+static int generate_iovs(struct vma_area *vma, int pagemap,
+			 struct page_pipe *pp, u64 *map,
+			 unsigned long *nr_pages)
 {
 	unsigned long pfn, nr_to_scan;
 	unsigned long pages[2] = {};
@@ -148,6 +150,7 @@ static int generate_iovs(struct vma_area *vma, int pagemap, struct page_pipe *pp
 			return -1;
 	}
 
+	*nr_pages = pages[1];
 	cnt_add(CNT_PAGES_SCANNED, nr_to_scan);
 	cnt_add(CNT_PAGES_SKIPPED_PARENT, pages[0]);
 	cnt_add(CNT_PAGES_WRITTEN, pages[1]);
@@ -156,35 +159,29 @@ static int generate_iovs(struct vma_area *vma, int pagemap, struct page_pipe *pp
 	return 0;
 }
 
+static void prep_mprotect_vma_entry(struct parasite_dump_pages_args *args,
+				    struct vma_area *vma_area)
+{
+	struct parasite_vma_entry *p_vma = &pargs_vmas(args)[args->nr_vmas];
+
+	BUG_ON((void *)p_vma > (void *)pargs_iovs(args));
+
+	p_vma->start	= vma_area->vma.start;
+	p_vma->len	= vma_area_len(vma_area);
+	p_vma->prot	= vma_area->vma.prot;
+
+	args->nr_vmas++;
+}
 static struct parasite_dump_pages_args *prep_dump_pages_args(struct parasite_ctl *ctl,
 		struct vm_area_list *vma_area_list)
 {
 	struct parasite_dump_pages_args *args;
-	struct parasite_vma_entry *p_vma;
-	struct vma_area *vma;
 
 	args = parasite_args_s(ctl, dump_pages_args_size(vma_area_list));
 
 	pargs_init_offs(args, vma_area_list->nr);
-	p_vma = pargs_vmas(args);
 	args->nr_vmas = 0;
 
-	list_for_each_entry(vma, &vma_area_list->h, list) {
-		if (!privately_dump_vma(vma))
-			continue;
-		if (vma->vma.prot & PROT_READ)
-			continue;
-
-		p_vma->start = vma->vma.start;
-		p_vma->len = vma_area_len(vma);
-		p_vma->prot = vma->vma.prot;
-
-		args->nr_vmas++;
-		p_vma++;
-	}
-
-	BUG_ON((void *)p_vma > (void *)pargs_iovs(args));
-
 	return args;
 }
 
@@ -198,6 +195,7 @@ static int __parasite_dump_pages_seized(struct parasite_ctl *ctl,
 	struct page_pipe *pp;
 	struct page_pipe_buf *ppb;
 	struct vma_area *vma_area;
+	unsigned long nr_pages = 0;
 	int ret = -1;
 
 	pr_info("\n");
@@ -229,21 +227,37 @@ static int __parasite_dump_pages_seized(struct parasite_ctl *ctl,
 		goto out_close;
 
 	/*
-	 * Step 1 -- generate the pagemap
+	 * Step 1 -- generate the pagemap, and figure out if VMA we're
+	 *           about to dump need to be mprotect'ed to achieve
+	 *           READ access.
 	 */
-
 	list_for_each_entry(vma_area, &vma_area_list->h, list) {
 		if (!privately_dump_vma(vma_area))
 			continue;
 
-		ret = generate_iovs(vma_area, pagemap, pp, map);
+		ret = generate_iovs(vma_area, pagemap, pp, map, &nr_pages);
 		if (ret < 0)
 			goto out_pp;
+
+		if (nr_pages && !(vma_area->vma.prot & PROT_READ))
+			prep_mprotect_vma_entry(args, vma_area);
 	}
 
 	debug_show_page_pipe(pp);
 
 	/*
+	 * Step 1.5 -- Call the parasite to grant us READ access to pages
+	 *             we're to dump but can't yet.
+	 */
+
+	args->add_prot = PROT_READ;
+	ret = parasite_execute_daemon(PARASITE_CMD_MPROTECT_VMAS, ctl);
+	if (ret) {
+		pr_err("Can't dump unprotect vmas with parasite\n");
+		goto out_pp;
+	}
+
+	/*
 	 * Step 2 -- grab pages into page-pipe
 	 */
 
@@ -294,6 +308,15 @@ static int __parasite_dump_pages_seized(struct parasite_ctl *ctl,
 	}
 
 	/*
+	 * Step 3.5 -- Restore protection if it was alternaed.
+	 */
+	args->add_prot = 0;
+	if (parasite_execute_daemon(PARASITE_CMD_MPROTECT_VMAS, ctl)) {
+		pr_err("Can't rollback unprotected vmas with parasite\n");
+		goto out_pp;
+	}
+
+	/*
 	 * Step 4 -- clean up
 	 */
 
@@ -318,31 +341,10 @@ int parasite_dump_pages_seized(struct parasite_ctl *ctl,
 
 	pargs = prep_dump_pages_args(ctl, vma_area_list);
 
-	/*
-	 * Add PROT_READ protection for all VMAs we're about to
-	 * dump if they don't have one. Otherwise we'll not be
-	 * able to read the memory contents.
-	 *
-	 * Afterwards -- reprotect memory back.
-	 */
-
-	pargs->add_prot = PROT_READ;
-	ret = parasite_execute_daemon(PARASITE_CMD_MPROTECT_VMAS, ctl);
-	if (ret) {
-		pr_err("Can't dump unprotect vmas with parasite\n");
-		return ret;
-	}
-
 	ret = __parasite_dump_pages_seized(ctl, pargs, vma_area_list, pp);
 	if (ret)
 		pr_err("Can't dump page with parasite\n");
 
-	pargs->add_prot = 0;
-	if (parasite_execute_daemon(PARASITE_CMD_MPROTECT_VMAS, ctl)) {
-		pr_err("Can't rollback unprotected vmas with parasite\n");
-		ret = -1;
-	}
-
 	return ret;
 }
 
-- 
1.8.3.1



More information about the CRIU mailing list