mirror of
https://github.com/checkpoint-restore/criu.git
synced 2026-01-23 02:14:37 +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:
parent
6edcef7406
commit
70c8c12c64
6 changed files with 48 additions and 12 deletions
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue