forked from cyrillos/criu
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cr-exec.c
128 lines (104 loc) · 2.56 KB
/
cr-exec.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include <unistd.h>
#include <string.h>
#include "crtools.h"
#include "ptrace.h"
#include "parasite-syscall.h"
struct syscall_exec_desc {
char *name;
unsigned nr;
};
static struct syscall_exec_desc sc_exec_table[] = {
#define SYSCALL(__name, __nr) { .name = #__name, .nr = __nr, },
#include "sys-exec-tbl.c"
#undef SYSCALL
{ }, /* terminator */
};
static struct syscall_exec_desc *find_syscall(char *name)
{
int i;
for (i = 0; sc_exec_table[i].name != NULL; i++)
if (!strcmp(sc_exec_table[i].name, name))
return &sc_exec_table[i];
return NULL;
}
#define MAX_ARGS 6
static int execute_syscall(struct parasite_ctl *ctl,
struct syscall_exec_desc *scd, char **opt)
{
int i, err;
unsigned long args[MAX_ARGS] = {}, ret, r_mem_size = 0;
void *r_mem = NULL;
for (i = 0; i < MAX_ARGS; i++) {
if (opt[i] == NULL)
break;
if (opt[i][0] == '&') {
int len;
if (!r_mem) {
err = parasite_map_exchange(ctl, PAGE_SIZE);
if (err)
return err;
r_mem_size = PAGE_SIZE;
r_mem = ctl->local_map;
}
len = strlen(opt[i]);
if (r_mem_size < len) {
pr_err("Arg size overflow\n");
return -1;
}
memcpy(r_mem, opt[i] + 1, len);
args[i] = (unsigned long)ctl->remote_map + (r_mem - ctl->local_map);
pr_info("Pushing mem arg [%s]\n", (char *)r_mem);
r_mem_size -= len;
r_mem += len;
} else
args[i] = strtol(opt[i], NULL, 0);
}
pr_info("Calling %d with %lu %lu %lu %lu %lu %lu\n", scd->nr,
args[0], args[1], args[2], args[3], args[4], args[5]);
err = syscall_seized(ctl, scd->nr, &ret,
args[0], args[1], args[2], args[3], args[4], args[5]);
if (err)
return err;
pr_msg("Syscall returned %lx(%d)\n", ret, (int)ret);
return 0;
}
int cr_exec(int pid, char **opt)
{
char *sys_name = opt[0];
struct syscall_exec_desc *si;
struct parasite_ctl *ctl;
struct vm_area_list vmas;
int ret = -1, prev_state;
if (!sys_name) {
pr_err("Syscall name required\n");
goto out;
}
si = find_syscall(sys_name);
if (!si) {
pr_err("Unknown syscall [%s]\n", sys_name);
goto out;
}
prev_state = ret = seize_task(pid, -1, NULL, NULL);
if (ret < 0) {
pr_err("Can't seize task %d\n", pid);
goto out;
}
ret = collect_mappings(pid, &vmas);
if (ret) {
pr_err("Can't collect vmas for %d\n", pid);
goto out_unseize;
}
ctl = parasite_prep_ctl(pid, &vmas);
if (!ctl) {
pr_err("Can't prep ctl %d\n", pid);
goto out_unseize;
}
ret = execute_syscall(ctl, si, opt + 1);
if (ret < 0)
pr_err("Can't execute syscall remotely\n");
parasite_cure_seized(ctl, NULL);
out_unseize:
unseize_task(pid, prev_state);
out:
return ret;
}