2
0
mirror of https://github.com/checkpoint-restore/criu synced 2025-08-31 14:25:49 +00:00

compel: don't mmap parasite as RWX

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 <mclapinski@google.com>
This commit is contained in:
Michał Cłapiński
2020-10-13 01:47:09 +02:00
committed by Andrei Vagin
parent 6edcef7406
commit 70c8c12c64
6 changed files with 48 additions and 12 deletions

View File

@@ -12,7 +12,7 @@ SECTIONS
*(.compel.init)
}
.data : {
.data : ALIGN(0x10000) {
*(.data*)
*(.bss*)
}

View File

@@ -12,7 +12,7 @@ SECTIONS
*(.compel.init)
}
.data : {
.data : ALIGN(0x1000) {
*(.data*)
*(.bss*)
}

View File

@@ -13,7 +13,7 @@ SECTIONS
*(.compel.init)
}
.data : {
.data : ALIGN(0x1000) {
*(.data*)
*(.bss*)
}

View File

@@ -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;

View File

@@ -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",

View File

@@ -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;
}