Commit bb33a774 authored by Pavel Emelyanov's avatar Pavel Emelyanov Committed by Andrei Vagin

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: 's avatarPavel Emelyanov <xemul@virtuozzo.com>
Signed-off-by: 's avatarAndrei Vagin <avagin@virtuozzo.com>
parent 27243759
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
#include "int.h" #include "int.h"
#include "types.h" #include "types.h"
#include "crtools.h" #include "crtools.h"
#include "parasite-syscall.h"
#include "proc_parse.h" #include "proc_parse.h"
#include "ptrace.h" #include "ptrace.h"
#include "pstree.h" #include "pstree.h"
#include "parasite-syscall.h"
#include "vma.h" #include "vma.h"
#include "log.h" #include "log.h"
#include "util.h" #include "util.h"
......
...@@ -32,4 +32,6 @@ struct parasite_ctl { ...@@ -32,4 +32,6 @@ struct parasite_ctl {
int tsock; /* transport socket for transferring fds */ 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 #endif
...@@ -23,4 +23,8 @@ extern int compel_wait_task(int pid, int ppid, ...@@ -23,4 +23,8 @@ extern int compel_wait_task(int pid, int ppid,
#define TASK_STOPPED 0x3 #define TASK_STOPPED 0x3
#define TASK_ZOMBIE 0x6 #define TASK_ZOMBIE 0x6
struct parasite_ctl;
extern int compel_infect(struct parasite_ctl *ctl, unsigned long nr_threads, unsigned long args_size);
#endif #endif
...@@ -6,12 +6,23 @@ ...@@ -6,12 +6,23 @@
#include <signal.h> #include <signal.h>
#include <linux/seccomp.h> #include <linux/seccomp.h>
#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 "parasite-syscall.h"
#include "pie-relocs.h"
#include "parasite-blob.h"
#include "sigframe.h"
#include "log.h" #include "log.h"
#include "infect.h" #include "infect.h"
#include "infect-priv.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 #define PTRACE_EVENT_STOP 128
#ifndef SECCOMP_MODE_DISABLED #ifndef SECCOMP_MODE_DISABLED
...@@ -230,3 +241,289 @@ err: ...@@ -230,3 +241,289 @@ err:
return -1; 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, &regs, &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;
}
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
#include "imgset.h" #include "imgset.h"
#include "ptrace.h" #include "ptrace.h"
#include "parasite-syscall.h" #include "parasite-syscall.h"
#include "parasite-blob.h"
#include "parasite.h" #include "parasite.h"
#include "crtools.h" #include "crtools.h"
#include "namespaces.h" #include "namespaces.h"
...@@ -42,6 +41,7 @@ ...@@ -42,6 +41,7 @@
#include "restorer.h" #include "restorer.h"
#include "pie/pie-relocs.h" #include "pie/pie-relocs.h"
#include "infect.h"
#include "infect-priv.h" #include "infect-priv.h"
#define MEMFD_FNAME "CRIUMFD" #define MEMFD_FNAME "CRIUMFD"
...@@ -126,39 +126,6 @@ bool seized_native(struct parasite_ctl *ctl) ...@@ -126,39 +126,6 @@ bool seized_native(struct parasite_ctl *ctl)
{ {
return user_regs_native(&ctl->orig.regs); 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 */ /* we run at @regs->ip */
static int parasite_trap(struct parasite_ctl *ctl, pid_t pid, 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) ...@@ -296,7 +263,7 @@ static int __parasite_send_cmd(int sockfd, struct ctl_msg *m)
return 0; 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; int ret;
...@@ -364,20 +331,6 @@ int parasite_execute_daemon(unsigned int cmd, struct parasite_ctl *ctl) ...@@ -364,20 +331,6 @@ int parasite_execute_daemon(unsigned int cmd, struct parasite_ctl *ctl)
return ret; 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) int parasite_send_fd(struct parasite_ctl *ctl, int fd)
{ {
if (send_fd(ctl->tsock, NULL, 0, fd) < 0) { if (send_fd(ctl->tsock, NULL, 0, fd) < 0) {
...@@ -418,23 +371,6 @@ static void sigchld_handler(int signal, siginfo_t *siginfo, void *data) ...@@ -418,23 +371,6 @@ static void sigchld_handler(int signal, siginfo_t *siginfo, void *data)
exit(1); 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() static int restore_child_handler()
{ {
struct sigaction sa = { struct sigaction sa = {
...@@ -452,121 +388,6 @@ static int restore_child_handler() ...@@ -452,121 +388,6 @@ static int restore_child_handler()
return 0; 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, &regs, &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) static int alloc_groups_copy_creds(CredsEntry *ce, struct parasite_dump_creds *c)
{ {
BUILD_BUG_ON(sizeof(ce->groups[0]) != sizeof(c->groups[0])); BUILD_BUG_ON(sizeof(ce->groups[0]) != sizeof(c->groups[0]));
...@@ -1426,53 +1247,11 @@ void parasite_ensure_args_size(unsigned long sz) ...@@ -1426,53 +1247,11 @@ void parasite_ensure_args_size(unsigned long sz)
parasite_args_size = 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) 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); 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 parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item,
struct vm_area_list *vma_area_list) struct vm_area_list *vma_area_list)
{ {
...@@ -1507,7 +1286,7 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item, ...@@ -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(dump_pages_args_size(vma_area_list));
parasite_ensure_args_size(aio_rings_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); parasite_cure_seized(ctl);
return NULL; return NULL;
} }
...@@ -1519,72 +1298,6 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item, ...@@ -1519,72 +1298,6 @@ struct parasite_ctl *parasite_infect_seized(pid_t pid, struct pstree_item *item,
return ctl; 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 ptrace_stop_pie(pid_t pid, void *addr, enum trace_flags *tf)
{ {
int ret; int ret;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment