diff --git a/criu/Makefile.crtools b/criu/Makefile.crtools index 51573cd43..5c2911ffc 100644 --- a/criu/Makefile.crtools +++ b/criu/Makefile.crtools @@ -86,7 +86,9 @@ ifeq ($(VDSO),y) obj-y += pie-util-vdso.o obj-y += vdso.o obj-y += pie-util-vdso-elf32.o -CFLAGS_pie-util-vdso-elf32.o += -DCONFIG_VDSO_32 +CFLAGS_pie-util-vdso-elf32.o += -DCONFIG_VDSO_32 +obj-$(CONFIG_COMPAT) += vdso-compat.o +CFLAGS_REMOVE_vdso-compat.o += $(CFLAGS-ASAN) $(CFLAGS-GCOV) endif PROTOBUF_GEN := scripts/protobuf-gen.sh diff --git a/criu/include/vdso.h b/criu/include/vdso.h index 9b6010c03..e118ed7f3 100644 --- a/criu/include/vdso.h +++ b/criu/include/vdso.h @@ -18,6 +18,11 @@ extern int vdso_init(void); extern int parasite_fixup_vdso(struct parasite_ctl *ctl, pid_t pid, struct vm_area_list *vma_area_list); +#ifdef CONFIG_COMPAT +void compat_vdso_helper(struct vdso_symtable *native, int pipe_fd, + int err_fd, void *vdso_buf, size_t buf_size); +#endif + #else /* CONFIG_VDSO */ #define vdso_init() (0) diff --git a/criu/vdso-compat.c b/criu/vdso-compat.c new file mode 100644 index 000000000..7669eb3df --- /dev/null +++ b/criu/vdso-compat.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include + +#include "types.h" +#include "parasite-syscall.h" +#include "parasite.h" +#include "vdso.h" + +static void exit_on(int ret, int err_fd, char *reason) +{ + if (ret) { + syscall(__NR_write, err_fd, reason, strlen(reason)); + syscall(__NR_exit, ret); + } +} +/* + * Because of restrictions of ARCH_MAP_VDSO_* API, new vDSO blob + * can be mapped only if there is no vDSO blob present for a process. + * This is a helper process, it unmaps 64-bit vDSO and maps 32-bit vDSO. + * Then it copies vDSO blob to shared with CRIU mapping. + * + * The purpose is to fill compat vdso's symtable (vdso_compat_rt). + * It's an optimization to fill symtable only once at CRIU restore + * for all restored tasks. + * + * @native - 64-bit vDSO blob (for easy unmap) + * @pipe_fd - to get size of compat blob from /proc/.../maps + * @err_fd - to print error messages + * @vdso_buf, buf_size - shared with CRIU buffer + * + * WARN: This helper shouldn't call pr_err() or any syscall with + * Glibc's wrapper function - it may very likely blow up. + */ +void compat_vdso_helper(struct vdso_symtable *native, int pipe_fd, + int err_fd, void *vdso_buf, size_t buf_size) +{ + size_t vma_size; + void *vdso_addr; + long vdso_size; + long ret; + + vma_size = native->vma_end - native->vma_start; + ret = syscall(__NR_munmap, native->vma_start, vma_size); + exit_on(ret, err_fd, "Error: Failed to unmap native vdso\n"); + + vma_size = native->vvar_end - native->vvar_start; + ret = syscall(__NR_munmap, native->vvar_start, vma_size); + exit_on(ret, err_fd, "Error: Failed to unmap native vvar\n"); + + ret = syscall(__NR_arch_prctl, ARCH_MAP_VDSO_32, native->vma_start); + if (ret < 0) + exit_on(ret, err_fd, "Error: ARCH_MAP_VDSO failed\n"); + + vdso_size = ret; + if (vdso_size > buf_size) + exit_on(-1, err_fd, "Error: Compatible vdso's size is bigger than reserved buf\n"); + + /* Stop so CRIU could parse smaps to find 32-bit vdso's size */ + ret = syscall(__NR_kill, syscall(__NR_getpid), SIGSTOP); + exit_on(ret, err_fd, "Error: Can't stop myself with SIGSTOP (having a good time)\n"); + + ret = syscall(__NR_read, pipe_fd, &vdso_addr, sizeof(void *)); + if (ret != sizeof(void *)) + exit_on(-1, err_fd, "Error: Can't read size of mmaped vdso from pipe\n"); + + memcpy(vdso_buf, vdso_addr, vdso_size); + + syscall(__NR_exit, 0); +} diff --git a/criu/vdso.c b/criu/vdso.c index 770853514..8bc7b5d3b 100644 --- a/criu/vdso.c +++ b/criu/vdso.c @@ -330,68 +330,6 @@ static int vdso_fill_self_symtable(struct vdso_symtable *s) } #ifdef CONFIG_COMPAT -static void exit_on(int ret, int err_fd, char *reason) -{ - if (ret) { - syscall(__NR_write, err_fd, reason, strlen(reason)); - syscall(__NR_exit, ret); - } -} -/* - * Because of restrictions of ARCH_MAP_VDSO_* API, new vDSO blob - * can be mapped only if there is no vDSO blob present for a process. - * This is a helper process, it unmaps 64-bit vDSO and maps 32-bit vDSO. - * Then it copies vDSO blob to shared with CRIU mapping. - * - * The purpose is to fill compat vdso's symtable (vdso_compat_rt). - * It's an optimization to fill symtable only once at CRIU restore - * for all restored tasks. - * - * @native - 64-bit vDSO blob (for easy unmap) - * @pipe_fd - to get size of compat blob from /proc/.../maps - * @err_fd - to print error messages - * @vdso_buf, buf_size - shared with CRIU buffer - * - * WARN: This helper shouldn't call pr_err() or any syscall with - * Glibc's wrapper function - it may very likely blow up. - */ -static void compat_vdso_helper(struct vdso_symtable *native, int pipe_fd, - int err_fd, void *vdso_buf, size_t buf_size) -{ - size_t vma_size; - void *vdso_addr; - long vdso_size; - long ret; - - vma_size = native->vma_end - native->vma_start; - ret = syscall(__NR_munmap, native->vma_start, vma_size); - exit_on(ret, err_fd, "Error: Failed to unmap native vdso\n"); - - vma_size = native->vvar_end - native->vvar_start; - ret = syscall(__NR_munmap, native->vvar_start, vma_size); - exit_on(ret, err_fd, "Error: Failed to unmap native vvar\n"); - - ret = syscall(__NR_arch_prctl, ARCH_MAP_VDSO_32, native->vma_start); - if (ret < 0) - exit_on(ret, err_fd, "Error: ARCH_MAP_VDSO failed\n"); - - vdso_size = ret; - if (vdso_size > buf_size) - exit_on(-1, err_fd, "Error: Compatible vdso's size is bigger than reserved buf\n"); - - /* Stop so CRIU could parse smaps to find 32-bit vdso's size */ - ret = syscall(__NR_kill, syscall(__NR_getpid), SIGSTOP); - exit_on(ret, err_fd, "Error: Can't stop myself with SIGSTOP (having a good time)\n"); - - ret = syscall(__NR_read, pipe_fd, &vdso_addr, sizeof(void *)); - if (ret != sizeof(void *)) - exit_on(-1, err_fd, "Error: Can't read size of mmaped vdso from pipe\n"); - - memcpy(vdso_buf, vdso_addr, vdso_size); - - syscall(__NR_exit, 0); -} - static int vdso_mmap_compat(struct vdso_symtable *native, struct vdso_symtable *compat, void *vdso_buf, size_t buf_size) { @@ -503,13 +441,13 @@ out_unmap: return ret; } -#else +#else /* CONFIG_COMPAT */ static int vdso_fill_compat_symtable(struct vdso_symtable *native, struct vdso_symtable *compat) { return 0; } -#endif +#endif /* CONFIG_COMPAT */ int vdso_init(void) {