[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