[CRIU] [PATCH 10/11] vdso/kdat: Add test for preserving "[vdso]" hint after mremap()

Dmitry Safonov dsafonov at virtuozzo.com
Mon Jul 17 15:40:01 MSK 2017


If it does preserve, we can omit checking pagemap for dumpee or
filling symtable in parasite.

Signed-off-by: Dmitry Safonov <dsafonov at virtuozzo.com>
---
 criu/include/kerndat.h |  1 +
 criu/include/vdso.h    |  2 ++
 criu/kerndat.c         |  3 ++
 criu/vdso.c            | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 81 insertions(+)

diff --git a/criu/include/kerndat.h b/criu/include/kerndat.h
index 2667758906b7..8675e0a88f4f 100644
--- a/criu/include/kerndat.h
+++ b/criu/include/kerndat.h
@@ -62,6 +62,7 @@ struct kerndat_s {
 	unsigned long files_stat_max_files;
 	bool has_pid_for_children_ns;
 	bool can_map_vdso;
+	bool vdso_hint_reliable;
 #ifdef CONFIG_VDSO
 	struct vdso_symtable	vdso_sym;
 #ifdef CONFIG_COMPAT
diff --git a/criu/include/vdso.h b/criu/include/vdso.h
index b6110e442e04..3c4c0ecbdd17 100644
--- a/criu/include/vdso.h
+++ b/criu/include/vdso.h
@@ -16,6 +16,7 @@ extern struct vdso_maps vdso_maps_compat;
 extern int vdso_init_dump(void);
 extern int vdso_init_restore(void);
 extern int kerndat_vdso_fill_symtable(void);
+extern int kerndat_vdso_preserves_hint(void);
 
 extern int parasite_fixup_vdso(struct parasite_ctl *ctl, pid_t pid,
 			       struct vm_area_list *vma_area_list);
@@ -30,6 +31,7 @@ extern void compat_vdso_helper(struct vdso_maps *native, int pipe_fd,
 #define vdso_init_dump()					(0)
 #define vdso_init_restore()					(0)
 #define kerndat_vdso_fill_symtable()				(0)
+#define kerndat_vdso_preserves_hint()				(0)
 #define parasite_fixup_vdso(ctl, pid, vma_area_list)		(0)
 
 #endif /* CONFIG_VDSO */
diff --git a/criu/kerndat.c b/criu/kerndat.c
index 5ad5256a6d96..142e67e7b495 100644
--- a/criu/kerndat.c
+++ b/criu/kerndat.c
@@ -1059,6 +1059,9 @@ int kerndat_init(void)
 	/* Needs kdat.compat_cr filled before */
 	if (!ret)
 		ret = kerndat_vdso_fill_symtable();
+	/* Depends on kerndat_vdso_fill_symtable() */
+	if (!ret)
+		ret = kerndat_vdso_preserves_hint();
 	if (!ret)
 		ret = kerndat_detect_stack_guard_gap();
 	if (!ret)
diff --git a/criu/vdso.c b/criu/vdso.c
index 64046bc169d9..013a9a6c2c0a 100644
--- a/criu/vdso.c
+++ b/criu/vdso.c
@@ -621,3 +621,78 @@ int kerndat_vdso_fill_symtable(void)
 
 	return 0;
 }
+
+/*
+ * On x86 pre-v3.16 kernels can lose "[vdso]" hint
+ * in /proc/.../maps file after mremap()'ing vdso vma.
+ * Depends on kerndat_vdso_fill_symtable() - assuming that
+ * vdso_maps and vdso_maps_compat are filled.
+ */
+int kerndat_vdso_preserves_hint(void)
+{
+	struct vdso_maps vdso_maps_after;
+	int status, ret = -1;
+	pid_t child;
+
+	kdat.vdso_hint_reliable = 0;
+
+	if (vdso_maps.vdso_start == VDSO_BAD_ADDR)
+		return 0;
+
+	child = fork();
+	if (child < 0) {
+		pr_perror("fork() failed");
+		return -1;
+	}
+
+	if (child == 0) {
+		unsigned long vdso_addr = vdso_maps.vdso_start;
+		unsigned long vdso_size = vdso_maps.sym.vdso_size;
+		void *new_addr;
+
+		new_addr = mmap(0, vdso_size, PROT_NONE,
+				MAP_ANON | MAP_PRIVATE, -1, 0);
+		if (new_addr == MAP_FAILED)
+			exit(1);
+
+		child = getpid();
+		new_addr = (void *)syscall(SYS_mremap, vdso_addr, vdso_size,
+			vdso_size, MREMAP_MAYMOVE | MREMAP_FIXED, new_addr);
+		if (new_addr == MAP_FAILED)
+			syscall(SYS_exit, 2);
+		syscall(SYS_kill, child, SIGSTOP);
+		syscall(SYS_exit, 3);
+	}
+
+	waitpid(child, &status, WUNTRACED);
+	if (WIFEXITED(status)) {
+		int ret = WEXITSTATUS(status);
+
+		pr_err("Child unexpectedly exited with %d\n", ret);
+		goto out;
+	} else if (WIFSIGNALED(status)) {
+		int sig = WTERMSIG(status);
+
+		pr_err("Child unexpectedly signaled with %d: %s\n",
+				sig, strsignal(sig));
+		goto out;
+	} else if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP) {
+		pr_err("Child is unstoppable or was stopped by other means\n");
+		goto out_kill;
+	}
+
+	if (vdso_parse_maps(child, &vdso_maps_after)) {
+		pr_err("Failed parsing maps for child helper\n");
+		goto out_kill;
+	}
+
+	if (vdso_maps_after.vdso_start != VDSO_BAD_ADDR)
+		kdat.vdso_hint_reliable = 1;
+
+	ret = 0;
+out_kill:
+	kill(child, SIGKILL);
+	waitpid(child, &status, 0);
+out:
+	return ret;
+}
-- 
2.13.1



More information about the CRIU mailing list