From 2ec05a0563a6700bd6f8372d95e1205e285d9a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20C=C5=82api=C5=84ski?= Date: Tue, 13 Oct 2020 01:47:09 +0200 Subject: [PATCH] compel: don't mmap parasite as RWX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some kernels have W^X mitigation, which means they won't execute memory blocks if that memory block is also writable or ever was writable. This patch enables CRIU to run on such kernels. 1. Align .data section to a page. 2. mmap a memory block for parasite as RX. 3. mprotect everything after .text as RW. Signed-off-by: Michał Cłapiński --- compel/arch/ppc64/scripts/compel-pack.lds.S | 2 +- compel/arch/s390/scripts/compel-pack.lds.S | 2 +- compel/arch/x86/scripts/compel-pack.lds.S | 2 +- compel/include/uapi/infect.h | 1 + compel/src/lib/handle-elf.c | 5 +++ compel/src/lib/infect.c | 48 +++++++++++++++++---- 6 files changed, 48 insertions(+), 12 deletions(-) diff --git a/compel/arch/ppc64/scripts/compel-pack.lds.S b/compel/arch/ppc64/scripts/compel-pack.lds.S index 572f1c42d..f197fb999 100644 --- a/compel/arch/ppc64/scripts/compel-pack.lds.S +++ b/compel/arch/ppc64/scripts/compel-pack.lds.S @@ -12,7 +12,7 @@ SECTIONS *(.compel.init) } - .data : { + .data : ALIGN(0x10000) { *(.data*) *(.bss*) } diff --git a/compel/arch/s390/scripts/compel-pack.lds.S b/compel/arch/s390/scripts/compel-pack.lds.S index 2571ac852..a82118983 100644 --- a/compel/arch/s390/scripts/compel-pack.lds.S +++ b/compel/arch/s390/scripts/compel-pack.lds.S @@ -12,7 +12,7 @@ SECTIONS *(.compel.init) } - .data : { + .data : ALIGN(0x1000) { *(.data*) *(.bss*) } diff --git a/compel/arch/x86/scripts/compel-pack.lds.S b/compel/arch/x86/scripts/compel-pack.lds.S index 68ddb3622..44e705e29 100644 --- a/compel/arch/x86/scripts/compel-pack.lds.S +++ b/compel/arch/x86/scripts/compel-pack.lds.S @@ -13,7 +13,7 @@ SECTIONS *(.compel.init) } - .data : { + .data : ALIGN(0x1000) { *(.data*) *(.bss*) } diff --git a/compel/include/uapi/infect.h b/compel/include/uapi/infect.h index 257658a70..fa326eafe 100644 --- a/compel/include/uapi/infect.h +++ b/compel/include/uapi/infect.h @@ -155,6 +155,7 @@ struct parasite_blob_desc { unsigned long args_ptr_off; unsigned long got_off; unsigned long args_off; + unsigned long data_off; compel_reloc_t *relocs; unsigned int nr_relocs; } hdr; diff --git a/compel/src/lib/handle-elf.c b/compel/src/lib/handle-elf.c index 2319170ae..e505f2c43 100644 --- a/compel/src/lib/handle-elf.c +++ b/compel/src/lib/handle-elf.c @@ -155,6 +155,7 @@ int __handle_elf(void *mem, size_t size) int64_t toc_offset = 0; #endif int ret = -EINVAL; + unsigned long data_off = 0; pr_debug("Header\n"); pr_debug("------------\n"); @@ -698,6 +699,9 @@ int __handle_elf(void *mem, size_t size) pr_out("\n\t"); pr_out("0x%02x,", shdata[j]); } + + if (!strcmp(&secstrings[sh->sh_name], ".data")) + data_off = sh->sh_addr; } pr_out("};\n"); pr_out("\n"); @@ -722,6 +726,7 @@ int __handle_elf(void *mem, size_t size) pr_out("\tpbd->hdr.args_ptr_off = %s_sym__export_parasite_service_args_ptr;\n", opts.prefix); pr_out("\tpbd->hdr.got_off = round_up(pbd->hdr.bsize, sizeof(long));\n"); pr_out("\tpbd->hdr.args_off = pbd->hdr.got_off + %zd*sizeof(long);\n", nr_gotpcrel); + pr_out("\tpbd->hdr.data_off = %#lx;\n", data_off); pr_out("\tpbd->hdr.relocs = %s_relocs;\n", opts.prefix); pr_out("\tpbd->hdr.nr_relocs = " "sizeof(%s_relocs) / sizeof(%s_relocs[0]);\n", diff --git a/compel/src/lib/infect.c b/compel/src/lib/infect.c index 38846c21f..5c4c1b18d 100644 --- a/compel/src/lib/infect.c +++ b/compel/src/lib/infect.c @@ -690,12 +690,11 @@ static int parasite_start_daemon(struct parasite_ctl *ctl) return 0; } -static int parasite_mmap_exchange(struct parasite_ctl *ctl, unsigned long size) +static int parasite_mmap_exchange(struct parasite_ctl *ctl, unsigned long size, int remote_prot) { int fd; - ctl->remote_map = remote_mmap(ctl, NULL, size, - PROT_READ | PROT_WRITE | PROT_EXEC, + ctl->remote_map = remote_mmap(ctl, NULL, size, remote_prot, MAP_ANONYMOUS | MAP_SHARED, -1, 0); if (!ctl->remote_map) { pr_err("Can't allocate memory for parasite blob (pid: %d)\n", ctl->rpid); @@ -733,7 +732,7 @@ static void parasite_memfd_close(struct parasite_ctl *ctl, int fd) pr_err("Can't close memfd\n"); } -static int parasite_memfd_exchange(struct parasite_ctl *ctl, unsigned long size) +static int parasite_memfd_exchange(struct parasite_ctl *ctl, unsigned long size, int remote_prot) { void *where = (void *)ctl->ictx.syscall_ip + BUILTIN_SYSCALL_SIZE; bool compat_task = !compel_mode_native(ctl); @@ -785,8 +784,7 @@ static int parasite_memfd_exchange(struct parasite_ctl *ctl, unsigned long size) goto err_cure; } - ctl->remote_map = remote_mmap(ctl, NULL, size, - PROT_READ | PROT_WRITE | PROT_EXEC, + ctl->remote_map = remote_mmap(ctl, NULL, size, remote_prot, MAP_FILE | MAP_SHARED, fd, 0); if (!ctl->remote_map) { pr_err("Can't rmap memfd for parasite blob\n"); @@ -856,15 +854,47 @@ void compel_relocs_apply(void *mem, void *vbase, struct parasite_blob_desc *pbd) #endif } +long remote_mprotect(struct parasite_ctl *ctl, void *addr, size_t len, int prot) +{ + long ret; + int err; + bool compat_task = !user_regs_native(&ctl->orig.regs); + + err = compel_syscall(ctl, __NR(mprotect, compat_task), &ret, + (unsigned long)addr, len, prot, 0, 0, 0); + if (err < 0) { + pr_err("compel_syscall for mprotect failed\n"); + return -1; + } + return ret; +} + static int compel_map_exchange(struct parasite_ctl *ctl, unsigned long size) { - int ret; + int ret, remote_prot; - ret = parasite_memfd_exchange(ctl, size); + if (ctl->pblob.hdr.data_off) + remote_prot = PROT_READ | PROT_EXEC; + else + remote_prot = PROT_READ | PROT_WRITE | PROT_EXEC; + + ret = parasite_memfd_exchange(ctl, size, remote_prot); if (ret == 1) { pr_info("MemFD parasite doesn't work, goto legacy mmap\n"); - ret = parasite_mmap_exchange(ctl, size); + ret = parasite_mmap_exchange(ctl, size, remote_prot); + if (ret) + return ret; } + + if (!ctl->pblob.hdr.data_off) + return 0; + + ret = remote_mprotect(ctl, ctl->remote_map + ctl->pblob.hdr.data_off, + size - ctl->pblob.hdr.data_off, + PROT_READ | PROT_WRITE); + if (ret) + pr_err("remote_mprotect failed\n"); + return ret; }