From bb33a7742a7bcc50fd9f13625ef6f1d34b4f2b27 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Wed, 28 Sep 2016 11:29:01 +0300 Subject: [PATCH] infect: Move infect() into infect.c Not only infect() routine but all dependant code too. This is the core of the library actually. Signed-off-by: Pavel Emelyanov Signed-off-by: Andrei Vagin --- criu/cr-exec.c | 2 +- criu/include/infect-priv.h | 2 + criu/include/infect.h | 4 + criu/infect.c | 299 ++++++++++++++++++++++++++++++++++++- criu/parasite-syscall.c | 293 +----------------------------------- 5 files changed, 308 insertions(+), 292 deletions(-) diff --git a/criu/cr-exec.c b/criu/cr-exec.c index f07d71317..4762a8b90 100644 --- a/criu/cr-exec.c +++ b/criu/cr-exec.c @@ -4,10 +4,10 @@ #include "int.h" #include "types.h" #include "crtools.h" +#include "parasite-syscall.h" #include "proc_parse.h" #include "ptrace.h" #include "pstree.h" -#include "parasite-syscall.h" #include "vma.h" #include "log.h" #include "util.h" diff --git a/criu/include/infect-priv.h b/criu/include/infect-priv.h index a461ffa2c..8a1ab610d 100644 --- a/criu/include/infect-priv.h +++ b/criu/include/infect-priv.h @@ -32,4 +32,6 @@ struct parasite_ctl { int tsock; /* transport socket for transferring fds */ }; +int parasite_run(pid_t pid, int cmd, unsigned long ip, void *stack, + user_regs_struct_t *regs, struct thread_ctx *octx); #endif diff --git a/criu/include/infect.h b/criu/include/infect.h index dbd721b14..9cfbfe084 100644 --- a/criu/include/infect.h +++ b/criu/include/infect.h @@ -23,4 +23,8 @@ extern int compel_wait_task(int pid, int ppid, #define TASK_STOPPED 0x3 #define TASK_ZOMBIE 0x6 +struct parasite_ctl; + +extern int compel_infect(struct parasite_ctl *ctl, unsigned long nr_threads, unsigned long args_size); + #endif diff --git a/criu/infect.c b/criu/infect.c index 860525c92..3fc89df5d 100644 --- a/criu/infect.c +++ b/criu/infect.c @@ -6,12 +6,23 @@ #include #include +#include "ptrace.h" +#include "signal.h" +#include "asm/parasite-syscall.h" +#include "asm/dump.h" +#include "restorer.h" +#include "parasite.h" #include "parasite-syscall.h" - +#include "pie-relocs.h" +#include "parasite-blob.h" +#include "sigframe.h" #include "log.h" #include "infect.h" #include "infect-priv.h" +/* XXX will be removed soon */ +extern int parasite_wait_ack(int sockfd, unsigned int cmd, struct ctl_msg *m); + #define PTRACE_EVENT_STOP 128 #ifndef SECCOMP_MODE_DISABLED @@ -230,3 +241,289 @@ err: return -1; } +static int gen_parasite_saddr(struct sockaddr_un *saddr, int key) +{ + int sun_len; + + saddr->sun_family = AF_UNIX; + snprintf(saddr->sun_path, UNIX_PATH_MAX, + "X/crtools-pr-%d", key); + + sun_len = SUN_LEN(saddr); + *saddr->sun_path = '\0'; + + return sun_len; +} + +static int prepare_tsock(struct parasite_ctl *ctl, pid_t pid, + struct parasite_init_args *args) +{ + static int ssock = -1; + + pr_info("Putting tsock into pid %d\n", pid); + args->h_addr_len = gen_parasite_saddr(&args->h_addr, getpid()); + + if (ssock == -1) { + ssock = *ctl->ictx.p_sock; + if (ssock == -1) { + pr_err("No socket in ictx\n"); + goto err; + } + + *ctl->ictx.p_sock = -1; + + if (bind(ssock, (struct sockaddr *)&args->h_addr, args->h_addr_len) < 0) { + pr_perror("Can't bind socket"); + goto err; + } + + if (listen(ssock, 1)) { + pr_perror("Can't listen on transport socket"); + goto err; + } + } + + /* Check a case when parasite can't initialize a command socket */ + if (ctl->ictx.flags & INFECT_FAIL_CONNECT) + args->h_addr_len = gen_parasite_saddr(&args->h_addr, getpid() + 1); + + /* + * Set to -1 to prevent any accidental misuse. The + * only valid user of it is accept_tsock(). + */ + ctl->tsock = -ssock; + return 0; +err: + close_safe(&ssock); + return -1; +} + +static int setup_child_handler(struct parasite_ctl *ctl) +{ + struct sigaction sa = { + .sa_sigaction = ctl->ictx.child_handler, + .sa_flags = SA_SIGINFO | SA_RESTART, + }; + + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGCHLD); + if (sigaction(SIGCHLD, &sa, NULL)) { + pr_perror("Unable to setup SIGCHLD handler"); + return -1; + } + + return 0; +} + +int parasite_run(pid_t pid, int cmd, unsigned long ip, void *stack, + user_regs_struct_t *regs, struct thread_ctx *octx) +{ + k_rtsigset_t block; + + ksigfillset(&block); + if (ptrace(PTRACE_SETSIGMASK, pid, sizeof(k_rtsigset_t), &block)) { + pr_perror("Can't block signals for %d", pid); + goto err_sig; + } + + parasite_setup_regs(ip, stack, regs); + if (ptrace_set_regs(pid, regs)) { + pr_perror("Can't set registers for %d", pid); + goto err_regs; + } + + if (ptrace(cmd, pid, NULL, NULL)) { + pr_perror("Can't run parasite at %d", pid); + goto err_cont; + } + + return 0; + +err_cont: + if (ptrace_set_regs(pid, &octx->regs)) + pr_perror("Can't restore regs for %d", pid); +err_regs: + if (ptrace(PTRACE_SETSIGMASK, pid, sizeof(k_rtsigset_t), &octx->sigmask)) + pr_perror("Can't restore sigmask for %d", pid); +err_sig: + return -1; +} + +static int accept_tsock(struct parasite_ctl *ctl) +{ + int sock; + int ask = -ctl->tsock; /* this '-' is explained above */ + + sock = accept(ask, NULL, 0); + if (sock < 0) { + pr_perror("Can't accept connection to the transport socket"); + close(ask); + return -1; + } + + ctl->tsock = sock; + return 0; +} + +static int parasite_init_daemon(struct parasite_ctl *ctl) +{ + struct parasite_init_args *args; + pid_t pid = ctl->rpid; + user_regs_struct_t regs; + struct ctl_msg m = { }; + + *ctl->addr_cmd = PARASITE_CMD_INIT_DAEMON; + + args = parasite_args(ctl, struct parasite_init_args); + + args->sigframe = (uintptr_t)ctl->rsigframe; + args->log_level = log_get_loglevel(); + + futex_set(&args->daemon_connected, 0); + + if (prepare_tsock(ctl, pid, args)) + goto err; + + /* after this we can catch parasite errors in chld handler */ + if (setup_child_handler(ctl)) + goto err; + + regs = ctl->orig.regs; + if (parasite_run(pid, PTRACE_CONT, ctl->parasite_ip, ctl->rstack, ®s, &ctl->orig)) + goto err; + + futex_wait_while_eq(&args->daemon_connected, 0); + if (futex_get(&args->daemon_connected) != 1) { + errno = -(int)futex_get(&args->daemon_connected); + pr_perror("Unable to connect a transport socket"); + goto err; + } + + if (accept_tsock(ctl) < 0) + goto err; + + if (parasite_send_fd(ctl, log_get_fd())) + goto err; + + pr_info("Wait for parasite being daemonized...\n"); + + if (parasite_wait_ack(ctl->tsock, PARASITE_CMD_INIT_DAEMON, &m)) { + pr_err("Can't switch parasite %d to daemon mode %d\n", + pid, m.err); + goto err; + } + + ctl->sigreturn_addr = args->sigreturn_addr; + ctl->daemonized = true; + pr_info("Parasite %d has been switched to daemon mode\n", pid); + return 0; +err: + return -1; +} + +static int parasite_start_daemon(struct parasite_ctl *ctl) +{ + pid_t pid = ctl->rpid; + struct infect_ctx *ictx = &ctl->ictx; + + /* + * Get task registers before going daemon, since the + * get_task_regs needs to call ptrace on _stopped_ task, + * while in daemon it is not such. + */ + + if (get_task_regs(pid, ctl->orig.regs, ictx->save_regs, ictx->regs_arg)) { + pr_err("Can't obtain regs for thread %d\n", pid); + return -1; + } + + if (ictx->make_sigframe(ictx->regs_arg, ctl->sigframe, ctl->rsigframe, &ctl->orig.sigmask)) + return -1; + + if (parasite_init_daemon(ctl)) + return -1; + + return 0; +} + +#define init_parasite_ctl(ctl, blob_type) \ + do { \ + memcpy(ctl->local_map, parasite_##blob_type##_blob, \ + sizeof(parasite_##blob_type##_blob)); \ + ELF_RELOCS_APPLY(parasite_##blob_type, \ + ctl->local_map, ctl->remote_map); \ + /* Setup the rest of a control block */ \ + ctl->parasite_ip = (unsigned long)parasite_sym(ctl->remote_map, \ + blob_type, __export_parasite_head_start); \ + ctl->addr_cmd = parasite_sym(ctl->local_map, blob_type, \ + __export_parasite_cmd); \ + ctl->addr_args = parasite_sym(ctl->local_map, blob_type, \ + __export_parasite_args); \ + } while (0) + +int compel_infect(struct parasite_ctl *ctl, unsigned long nr_threads, unsigned long args_size) +{ + int ret; + unsigned long p, map_exchange_size, parasite_size = 0; + + if (!arch_can_dump_task(ctl)) + goto err; + + /* + * Inject a parasite engine. Ie allocate memory inside alien + * space and copy engine code there. Then re-map the engine + * locally, so we will get an easy way to access engine memory + * without using ptrace at all. + */ + + if (seized_native(ctl)) + parasite_size = pie_size(parasite_native); +#ifdef CONFIG_COMPAT + else + parasite_size = pie_size(parasite_compat); +#endif + + ctl->args_size = round_up(args_size, PAGE_SIZE); + parasite_size += ctl->args_size; + + map_exchange_size = parasite_size; + map_exchange_size += RESTORE_STACK_SIGFRAME + PARASITE_STACK_SIZE; + if (nr_threads > 1) + map_exchange_size += PARASITE_STACK_SIZE; + + ret = parasite_map_exchange(ctl, map_exchange_size); + if (ret) + goto err; + + pr_info("Putting parasite blob into %p->%p\n", ctl->local_map, ctl->remote_map); + + if (seized_native(ctl)) + init_parasite_ctl(ctl, native); +#ifdef CONFIG_COMPAT + else + init_parasite_ctl(ctl, compat); +#endif + + p = parasite_size; + + ctl->rsigframe = ctl->remote_map + p; + ctl->sigframe = ctl->local_map + p; + + p += RESTORE_STACK_SIGFRAME; + p += PARASITE_STACK_SIZE; + ctl->rstack = ctl->remote_map + p; + + if (nr_threads > 1) { + p += PARASITE_STACK_SIZE; + ctl->r_thread_stack = ctl->remote_map + p; + } + + if (parasite_start_daemon(ctl)) + goto err; + + return 0; + +err: + return -1; +} + diff --git a/criu/parasite-syscall.c b/criu/parasite-syscall.c index bbc9a9378..62841a2aa 100644 --- a/criu/parasite-syscall.c +++ b/criu/parasite-syscall.c @@ -16,7 +16,6 @@ #include "imgset.h" #include "ptrace.h" #include "parasite-syscall.h" -#include "parasite-blob.h" #include "parasite.h" #include "crtools.h" #include "namespaces.h" @@ -42,6 +41,7 @@ #include "restorer.h" #include "pie/pie-relocs.h" +#include "infect.h" #include "infect-priv.h" #define MEMFD_FNAME "CRIUMFD" @@ -126,39 +126,6 @@ bool seized_native(struct parasite_ctl *ctl) { return user_regs_native(&ctl->orig.regs); } -static int parasite_run(pid_t pid, int cmd, unsigned long ip, void *stack, - user_regs_struct_t *regs, struct thread_ctx *octx) -{ - k_rtsigset_t block; - - ksigfillset(&block); - if (ptrace(PTRACE_SETSIGMASK, pid, sizeof(k_rtsigset_t), &block)) { - pr_perror("Can't block signals for %d", pid); - goto err_sig; - } - - parasite_setup_regs(ip, stack, regs); - if (ptrace_set_regs(pid, regs)) { - pr_perror("Can't set registers for %d", pid); - goto err_regs; - } - - if (ptrace(cmd, pid, NULL, NULL)) { - pr_perror("Can't run parasite at %d", pid); - goto err_cont; - } - - return 0; - -err_cont: - if (ptrace_set_regs(pid, &octx->regs)) - pr_perror("Can't restore regs for %d", pid); -err_regs: - if (ptrace(PTRACE_SETSIGMASK, pid, sizeof(k_rtsigset_t), &octx->sigmask)) - pr_perror("Can't restore sigmask for %d", pid); -err_sig: - return -1; -} /* we run at @regs->ip */ static int parasite_trap(struct parasite_ctl *ctl, pid_t pid, @@ -296,7 +263,7 @@ static int __parasite_send_cmd(int sockfd, struct ctl_msg *m) return 0; } -static int parasite_wait_ack(int sockfd, unsigned int cmd, struct ctl_msg *m) +int parasite_wait_ack(int sockfd, unsigned int cmd, struct ctl_msg *m) { int ret; @@ -364,20 +331,6 @@ int parasite_execute_daemon(unsigned int cmd, struct parasite_ctl *ctl) return ret; } -static int gen_parasite_saddr(struct sockaddr_un *saddr, int key) -{ - int sun_len; - - saddr->sun_family = AF_UNIX; - snprintf(saddr->sun_path, UNIX_PATH_MAX, - "X/crtools-pr-%d", key); - - sun_len = SUN_LEN(saddr); - *saddr->sun_path = '\0'; - - return sun_len; -} - int parasite_send_fd(struct parasite_ctl *ctl, int fd) { if (send_fd(ctl->tsock, NULL, 0, fd) < 0) { @@ -418,23 +371,6 @@ static void sigchld_handler(int signal, siginfo_t *siginfo, void *data) exit(1); } -static int setup_child_handler(struct parasite_ctl *ctl) -{ - struct sigaction sa = { - .sa_sigaction = ctl->ictx.child_handler, - .sa_flags = SA_SIGINFO | SA_RESTART, - }; - - sigemptyset(&sa.sa_mask); - sigaddset(&sa.sa_mask, SIGCHLD); - if (sigaction(SIGCHLD, &sa, NULL)) { - pr_perror("Unable to setup SIGCHLD handler"); - return -1; - } - - return 0; -} - static int restore_child_handler() { struct sigaction sa = { @@ -452,121 +388,6 @@ static int restore_child_handler() return 0; } -static int prepare_tsock(struct parasite_ctl *ctl, pid_t pid, - struct parasite_init_args *args) -{ - static int ssock = -1; - - pr_info("Putting tsock into pid %d\n", pid); - args->h_addr_len = gen_parasite_saddr(&args->h_addr, getpid()); - - if (ssock == -1) { - ssock = *ctl->ictx.p_sock; - if (ssock == -1) { - pr_err("No socket in ictx\n"); - goto err; - } - - *ctl->ictx.p_sock = -1; - - if (bind(ssock, (struct sockaddr *)&args->h_addr, args->h_addr_len) < 0) { - pr_perror("Can't bind socket"); - goto err; - } - - if (listen(ssock, 1)) { - pr_perror("Can't listen on transport socket"); - goto err; - } - } - - /* Check a case when parasite can't initialize a command socket */ - if (ctl->ictx.flags & INFECT_FAIL_CONNECT) - args->h_addr_len = gen_parasite_saddr(&args->h_addr, getpid() + 1); - - /* - * Set to -1 to prevent any accidental misuse. The - * only valid user of it is accept_tsock(). - */ - ctl->tsock = -ssock; - return 0; -err: - close_safe(&ssock); - return -1; -} - -static int accept_tsock(struct parasite_ctl *ctl) -{ - int sock; - int ask = -ctl->tsock; /* this '-' is explained above */ - - sock = accept(ask, NULL, 0); - if (sock < 0) { - pr_perror("Can't accept connection to the transport socket"); - close(ask); - return -1; - } - - ctl->tsock = sock; - return 0; -} - -static int parasite_init_daemon(struct parasite_ctl *ctl) -{ - struct parasite_init_args *args; - pid_t pid = ctl->rpid; - user_regs_struct_t regs; - struct ctl_msg m = { }; - - *ctl->addr_cmd = PARASITE_CMD_INIT_DAEMON; - - args = parasite_args(ctl, struct parasite_init_args); - - args->sigframe = (uintptr_t)ctl->rsigframe; - args->log_level = log_get_loglevel(); - - futex_set(&args->daemon_connected, 0); - - if (prepare_tsock(ctl, pid, args)) - goto err; - - /* after this we can catch parasite errors in chld handler */ - if (setup_child_handler(ctl)) - goto err; - - regs = ctl->orig.regs; - if (parasite_run(pid, PTRACE_CONT, ctl->parasite_ip, ctl->rstack, ®s, &ctl->orig)) - goto err; - - futex_wait_while_eq(&args->daemon_connected, 0); - if (futex_get(&args->daemon_connected) != 1) { - errno = -(int)futex_get(&args->daemon_connected); - pr_perror("Unable to connect a transport socket"); - goto err; - } - - if (accept_tsock(ctl) < 0) - goto err; - - if (parasite_send_fd(ctl, log_get_fd())) - goto err; - - pr_info("Wait for parasite being daemonized...\n"); - - if (parasite_wait_ack(ctl->tsock, PARASITE_CMD_INIT_DAEMON, &m)) { - pr_err("Can't switch parasite %d to daemon mode %d\n", - pid, m.err); - goto err; - } - - ctl->sigreturn_addr = args->sigreturn_addr; - ctl->daemonized = true; - pr_info("Parasite %d has been switched to daemon mode\n", pid); - return 0; -err: - return -1; -} - static int alloc_groups_copy_creds(CredsEntry *ce, struct parasite_dump_creds *c) { BUILD_BUG_ON(sizeof(ce->groups[0]) != sizeof(c->groups[0])); @@ -1426,53 +1247,11 @@ void parasite_ensure_args_size(unsigned long sz) parasite_args_size = sz; } -static int parasite_start_daemon(struct parasite_ctl *ctl) -{ - pid_t pid = ctl->rpid; - struct infect_ctx *ictx = &ctl->ictx; - - /* - * Get task registers before going daemon, since the - * get_task_regs needs to call ptrace on _stopped_ task, - * while in daemon it is not such. - */ - - if (get_task_regs(pid, ctl->orig.regs, ictx->save_regs, ictx->regs_arg)) { - pr_err("Can't obtain regs for thread %d\n", pid); - return -1; - } - - if (ictx->make_sigframe(ictx->regs_arg, ctl->sigframe, ctl->rsigframe, &ctl->orig.sigmask)) - return -1; - - if (parasite_init_daemon(ctl)) - return -1; - - return 0; -} - -#define init_parasite_ctl(ctl, blob_type) \ - do { \ - memcpy(ctl->local_map, parasite_##blob_type##_blob, \ - sizeof(parasite_##blob_type##_blob)); \ - ELF_RELOCS_APPLY(parasite_##blob_type, \ - ctl->local_map, ctl->remote_map); \ - /* Setup the rest of a control block */ \ - ctl->parasite_ip = (unsigned long)parasite_sym(ctl->remote_map, \ - blob_type, __export_parasite_head_start); \ - ctl->addr_cmd = parasite_sym(ctl->local_map, blob_type, \ - __export_parasite_cmd); \ - ctl->addr_args = parasite_sym(ctl->local_map, blob_type, \ - __export_parasite_args); \ - } while (0) - static int make_sigframe(void *arg, struct rt_sigframe *sf, struct rt_sigframe *rtsf, k_rtsigset_t *bs) { return construct_sigframe(sf, rtsf, bs, (CoreEntry *)arg); } -static int infect(struct parasite_ctl *ctl, unsigned long nr_threads, unsigned long args_size); - struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item, struct vm_area_list *vma_area_list) { @@ -1507,7 +1286,7 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item, parasite_ensure_args_size(dump_pages_args_size(vma_area_list)); parasite_ensure_args_size(aio_rings_args_size(vma_area_list)); - if (infect(ctl, item->nr_threads, parasite_args_size) < 0) { + if (compel_infect(ctl, item->nr_threads, parasite_args_size) < 0) { parasite_cure_seized(ctl); return NULL; } @@ -1519,72 +1298,6 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item, return ctl; } -static int infect(struct parasite_ctl *ctl, unsigned long nr_threads, unsigned long args_size) -{ - int ret; - unsigned long p, map_exchange_size, parasite_size = 0; - - if (!arch_can_dump_task(ctl)) - goto err; - - /* - * Inject a parasite engine. Ie allocate memory inside alien - * space and copy engine code there. Then re-map the engine - * locally, so we will get an easy way to access engine memory - * without using ptrace at all. - */ - - if (seized_native(ctl)) - parasite_size = pie_size(parasite_native); -#ifdef CONFIG_COMPAT - else - parasite_size = pie_size(parasite_compat); -#endif - - ctl->args_size = round_up(args_size, PAGE_SIZE); - parasite_size += ctl->args_size; - - map_exchange_size = parasite_size; - map_exchange_size += RESTORE_STACK_SIGFRAME + PARASITE_STACK_SIZE; - if (nr_threads > 1) - map_exchange_size += PARASITE_STACK_SIZE; - - ret = parasite_map_exchange(ctl, map_exchange_size); - if (ret) - goto err; - - pr_info("Putting parasite blob into %p->%p\n", ctl->local_map, ctl->remote_map); - - if (seized_native(ctl)) - init_parasite_ctl(ctl, native); -#ifdef CONFIG_COMPAT - else - init_parasite_ctl(ctl, compat); -#endif - - p = parasite_size; - - ctl->rsigframe = ctl->remote_map + p; - ctl->sigframe = ctl->local_map + p; - - p += RESTORE_STACK_SIGFRAME; - p += PARASITE_STACK_SIZE; - ctl->rstack = ctl->remote_map + p; - - if (nr_threads > 1) { - p += PARASITE_STACK_SIZE; - ctl->r_thread_stack = ctl->remote_map + p; - } - - if (parasite_start_daemon(ctl)) - goto err; - - return 0; - -err: - return -1; -} - int ptrace_stop_pie(pid_t pid, void *addr, enum trace_flags *tf) { int ret;