Branch data Line data Source code
1 : : #include <stdlib.h>
2 : : #include <stdio.h>
3 : : #include <stdarg.h>
4 : : #include <string.h>
5 : : #include <errno.h>
6 : : #include <unistd.h>
7 : : #include <stdbool.h>
8 : : #include <limits.h>
9 : : #include <signal.h>
10 : :
11 : : #include <sys/types.h>
12 : : #include <sys/time.h>
13 : : #include <sys/resource.h>
14 : : #include <sys/wait.h>
15 : :
16 : : #include "crtools.h"
17 : : #include "compiler.h"
18 : : #include "types.h"
19 : : #include "util.h"
20 : : #include "ptrace.h"
21 : : #include "proc_parse.h"
22 : :
23 : 391 : int unseize_task(pid_t pid, int st)
24 : : {
25 : 391 : pr_debug("\tUnseizeing %d into %d\n", pid, st);
26 : :
27 [ + - ]: 391 : if (st == TASK_DEAD)
28 : 391 : kill(pid, SIGKILL);
29 [ # # ]: 0 : else if (st == TASK_STOPPED)
30 : 0 : kill(pid, SIGSTOP);
31 [ # # ]: 0 : else if (st == TASK_ALIVE)
32 : : /* do nothing */ ;
33 : : else
34 : 0 : pr_err("Unknown final state %d\n", st);
35 : :
36 : 391 : return ptrace(PTRACE_DETACH, pid, NULL, NULL);
37 : : }
38 : :
39 : : /*
40 : : * This routine seizes task putting it into a special
41 : : * state where we can manipulate the task via ptrace
42 : : * inteface, and finally we can detach ptrace out of
43 : : * of it so the task would not know if it was saddled
44 : : * up with someone else.
45 : : */
46 : :
47 : 391 : int seize_task(pid_t pid, pid_t ppid, pid_t *pgid, pid_t *sid)
48 : : {
49 : : siginfo_t si;
50 : : int status;
51 : : int ret, ret2;
52 : : struct proc_pid_stat_small ps;
53 : :
54 : 391 : ret = ptrace(PTRACE_SEIZE, pid, NULL, 0);
55 : :
56 : : /*
57 : : * It's ugly, but the ptrace API doesn't allow to distinguish
58 : : * attaching to zombie from other errors. Thus we have to parse
59 : : * the target's /proc/pid/stat. Sad, but parse whatever else
60 : : * we might nead at that early point.
61 : : */
62 : :
63 : 391 : ret2 = parse_pid_stat_small(pid, &ps);
64 [ + - ]: 391 : if (ret2 < 0)
65 : : return -1;
66 : :
67 [ + + ]: 391 : if (pgid)
68 : 359 : *pgid = ps.pgid;
69 [ + + ]: 391 : if (sid)
70 : 359 : *sid = ps.sid;
71 : :
72 [ + + ]: 391 : if (ret < 0) {
73 [ - + ]: 10 : if (ps.state != 'Z') {
74 : 0 : pr_err("Unseizeable non-zombie %d found, state %c\n",
75 : : pid, ps.state);
76 : : return -1;
77 : : }
78 : :
79 : : return TASK_DEAD;
80 : : }
81 : :
82 [ + + ][ - + ]: 381 : if ((ppid != -1) && (ps.ppid != ppid)) {
83 : 0 : pr_err("Task pid reused while suspending (%d: %d -> %d)\n",
84 : : pid, ppid, ps.ppid);
85 : 381 : goto err;
86 : : }
87 : : try_again:
88 : 381 : ret = ptrace(PTRACE_INTERRUPT, pid, NULL, NULL);
89 [ - + ]: 381 : if (ret < 0) {
90 : 0 : pr_perror("SEIZE %d: can't interrupt task", pid);
91 : 0 : goto err;
92 : : }
93 : :
94 : 381 : ret = wait4(pid, &status, __WALL, NULL);
95 [ - + ]: 381 : if (ret < 0) {
96 : 0 : pr_perror("SEIZE %d: can't wait task", pid);
97 : 0 : goto err;
98 : : }
99 : :
100 [ - + ]: 381 : if (ret != pid) {
101 : 0 : pr_err("SEIZE %d: wrong task attached (%d)\n", pid, ret);
102 : 0 : goto err;
103 : : }
104 : :
105 [ - + ]: 381 : if (!WIFSTOPPED(status)) {
106 : 0 : pr_err("SEIZE %d: task not stopped after seize\n", pid);
107 : 0 : goto err;
108 : : }
109 : :
110 : 381 : ret = ptrace(PTRACE_GETSIGINFO, pid, NULL, &si);
111 [ - + ]: 381 : if (ret < 0) {
112 : 0 : pr_perror("SEIZE %d: can't read signfo", pid);
113 : 0 : goto err;
114 : : }
115 : :
116 [ - + ]: 381 : if (SI_EVENT(si.si_code) != PTRACE_EVENT_STOP) {
117 : : /*
118 : : * Kernel notifies us about the task being seized received some
119 : : * event other than the STOP, i.e. -- a signal. Let the task
120 : : * handle one and repeat.
121 : : */
122 : :
123 [ # # ]: 0 : if (ptrace(PTRACE_CONT, pid, NULL,
124 : 0 : (void *)(unsigned long)si.si_signo)) {
125 : 0 : pr_perror("Can't continue signal handling. Aborting.");
126 : 0 : goto err;
127 : : }
128 : :
129 : : goto try_again;
130 : : }
131 : :
132 [ - + ]: 381 : if (si.si_signo == SIGTRAP)
133 : : return TASK_ALIVE;
134 [ # # ]: 0 : else if (si.si_signo == SIGSTOP)
135 : : return TASK_STOPPED;
136 : :
137 : 0 : pr_err("SEIZE %d: unsupported stop signal %d\n", pid, si.si_signo);
138 : : err:
139 : 391 : unseize_task(pid, TASK_STOPPED);
140 : : return -1;
141 : : }
142 : :
143 : 349 : int ptrace_peek_area(pid_t pid, void *dst, void *addr, long bytes)
144 : : {
145 : : unsigned long w;
146 [ + - ]: 349 : if (bytes & (sizeof(long) - 1))
147 : : return -1;
148 [ + + ]: 698 : for (w = 0; w < bytes / sizeof(long); w++) {
149 : 349 : unsigned long *d = dst, *a = addr;
150 : 349 : d[w] = ptrace(PTRACE_PEEKDATA, pid, a + w, NULL);
151 [ # # ][ - + ]: 349 : if (d[w] == -1U && errno)
152 : : goto err;
153 : : }
154 : : return 0;
155 : : err:
156 : : return -2;
157 : : }
158 : :
159 : 698 : int ptrace_poke_area(pid_t pid, void *src, void *addr, long bytes)
160 : : {
161 : : unsigned long w;
162 [ + - ]: 698 : if (bytes & (sizeof(long) - 1))
163 : : return -1;
164 [ + + ]: 1396 : for (w = 0; w < bytes / sizeof(long); w++) {
165 : 698 : unsigned long *s = src, *a = addr;
166 [ + - ]: 698 : if (ptrace(PTRACE_POKEDATA, pid, a + w, s[w]))
167 : : goto err;
168 : : }
169 : : return 0;
170 : : err:
171 : : return -2;
172 : : }
173 : :
174 : : /* don't swap big space, it might overflow the stack */
175 : 349 : int ptrace_swap_area(pid_t pid, void *dst, void *src, long bytes)
176 : : {
177 : 349 : void *t = alloca(bytes);
178 : :
179 [ + - ]: 349 : if (ptrace_peek_area(pid, t, dst, bytes))
180 : : return -1;
181 : :
182 [ - + ]: 349 : if (ptrace_poke_area(pid, src, dst, bytes)) {
183 [ # # ]: 0 : if (ptrace_poke_area(pid, t, dst, bytes))
184 : : return -2;
185 : 0 : return -1;
186 : : }
187 : :
188 : 349 : memcpy(src, t, bytes);
189 : :
190 : 349 : return 0;
191 : : }
|