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:
committed by
Andrei Vagin
parent
6edcef7406
commit
70c8c12c64
@@ -12,7 +12,7 @@ SECTIONS
|
||||
*(.compel.init)
|
||||
}
|
||||
|
||||
.data : {
|
||||
.data : ALIGN(0x10000) {
|
||||
*(.data*)
|
||||
*(.bss*)
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@ SECTIONS
|
||||
*(.compel.init)
|
||||
}
|
||||
|
||||
.data : {
|
||||
.data : ALIGN(0x1000) {
|
||||
*(.data*)
|
||||
*(.bss*)
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@ SECTIONS
|
||||
*(.compel.init)
|
||||
}
|
||||
|
||||
.data : {
|
||||
.data : ALIGN(0x1000) {
|
||||
*(.data*)
|
||||
*(.bss*)
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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",
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user