File: | attach.c |
Location: | line 113, column 5 |
Description: | Function call argument is an uninitialized value |
1 | /* | |||
2 | * Copyright (C) 2011 by Nelson Elhage | |||
3 | * | |||
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |||
5 | * of this software and associated documentation files (the "Software"), to deal | |||
6 | * in the Software without restriction, including without limitation the rights | |||
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
8 | * copies of the Software, and to permit persons to whom the Software is | |||
9 | * furnished to do so, subject to the following conditions: | |||
10 | * | |||
11 | * The above copyright notice and this permission notice shall be included in | |||
12 | * all copies or substantial portions of the Software. | |||
13 | * | |||
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
20 | * THE SOFTWARE. | |||
21 | */ | |||
22 | #include <sys/types.h> | |||
23 | #include <dirent.h> | |||
24 | #include <sys/syscall.h> | |||
25 | #include <sys/mman.h> | |||
26 | #include <stdio.h> | |||
27 | #include <string.h> | |||
28 | #include <stdlib.h> | |||
29 | #include <fcntl.h> | |||
30 | #include <errno(*__errno_location ()).h> | |||
31 | #include <termios.h> | |||
32 | #include <sys/ioctl.h> | |||
33 | #include <sys/wait.h> | |||
34 | #include <signal.h> | |||
35 | #include <limits.h> | |||
36 | #include <time.h> | |||
37 | #include <sys/time.h> | |||
38 | #include <sys/stat.h> | |||
39 | ||||
40 | #include "ptrace.h" | |||
41 | #include "reptyr.h" | |||
42 | ||||
43 | #define TASK_COMM_LENGTH16 16 | |||
44 | struct proc_stat { | |||
45 | pid_t pid; | |||
46 | char comm[TASK_COMM_LENGTH16+1]; | |||
47 | char state; | |||
48 | pid_t ppid, sid, pgid; | |||
49 | dev_t ctty; | |||
50 | }; | |||
51 | ||||
52 | #define do_syscall(child, name, a0, a1, a2, a3, a4, a5)ptrace_remote_syscall((child), ptrace_syscall_numbers((child) )->nr_name, a0, a1, a2, a3, a4, a5) \ | |||
53 | ptrace_remote_syscall((child), ptrace_syscall_numbers((child))->nr_##name, \ | |||
54 | a0, a1, a2, a3, a4, a5) | |||
55 | ||||
56 | int parse_proc_stat(int statfd, struct proc_stat *out) { | |||
57 | char buf[1024]; | |||
58 | int n; | |||
59 | unsigned dev; | |||
60 | lseek(statfd, 0, SEEK_SET0); | |||
61 | if (read(statfd, buf, sizeof buf) < 0) | |||
62 | return errno(*__errno_location ()); | |||
63 | n = sscanf(buf, "%d (%16[^)]) %c %d %d %d %u", | |||
64 | &out->pid, out->comm, | |||
65 | &out->state, &out->ppid, &out->sid, | |||
66 | &out->pgid, &dev); | |||
67 | if (n == EOF(-1)) | |||
68 | return errno(*__errno_location ()); | |||
69 | if (n != 7) { | |||
70 | return EINVAL22; | |||
71 | } | |||
72 | out->ctty = dev; | |||
73 | return 0; | |||
74 | } | |||
75 | ||||
76 | int read_proc_stat(pid_t pid, struct proc_stat *out) { | |||
77 | char stat_path[PATH_MAX4096]; | |||
78 | int statfd; | |||
79 | int err; | |||
80 | ||||
81 | snprintf(stat_path, sizeof stat_path, "/proc/%d/stat", pid); | |||
82 | statfd = open(stat_path, O_RDONLY00); | |||
83 | if (statfd < 0) { | |||
84 | error("Unable to open %s: %s", stat_path, strerror(errno(*__errno_location ()))); | |||
85 | return -statfd; | |||
86 | } | |||
87 | ||||
88 | err = parse_proc_stat(statfd, out); | |||
89 | close(statfd); | |||
90 | return err; | |||
91 | } | |||
92 | ||||
93 | static void do_unmap(struct ptrace_child *child, child_addr_t addr, unsigned long len) { | |||
94 | if (addr == (unsigned long)-1) | |||
95 | return; | |||
96 | do_syscall(child, munmap, addr, len, 0, 0, 0, 0)ptrace_remote_syscall((child), ptrace_syscall_numbers((child) )->nr_munmap, addr, len, 0, 0, 0, 0); | |||
97 | } | |||
98 | ||||
99 | int *get_child_tty_fds(struct ptrace_child *child, int statfd, int *count) { | |||
100 | struct proc_stat child_status; | |||
101 | struct stat tty_st, console_st, st; | |||
102 | char buf[PATH_MAX4096]; | |||
103 | int n = 0, allocated = 0; | |||
104 | int *fds = NULL((void*)0); | |||
105 | DIR *dir; | |||
106 | struct dirent *d; | |||
107 | int *tmp = NULL((void*)0); | |||
108 | ||||
109 | debug("Looking up fds for tty in child."); | |||
110 | if ((child->error = parse_proc_stat(statfd, &child_status))) | |||
111 | return NULL((void*)0); | |||
112 | ||||
113 | debug("Resolved child tty: %x", (unsigned)child_status.ctty); | |||
| ||||
114 | ||||
115 | if (stat("/dev/tty", &tty_st) < 0) { | |||
116 | child->error = errno(*__errno_location ()); | |||
117 | error("Unable to stat /dev/tty"); | |||
118 | return NULL((void*)0); | |||
119 | } | |||
120 | ||||
121 | if (stat("/dev/console", &console_st) < 0) { | |||
122 | child->error = errno(*__errno_location ()); | |||
123 | error("Unable to stat /dev/console"); | |||
124 | return NULL((void*)0); | |||
125 | } | |||
126 | ||||
127 | snprintf(buf, sizeof buf, "/proc/%d/fd/", child->pid); | |||
128 | if ((dir = opendir(buf)) == NULL((void*)0)) | |||
129 | return NULL((void*)0); | |||
130 | while ((d = readdir(dir)) != NULL((void*)0)) { | |||
131 | if (d->d_name[0] == '.') continue; | |||
132 | snprintf(buf, sizeof buf, "/proc/%d/fd/%s", child->pid, d->d_name); | |||
133 | if (stat(buf, &st) < 0) | |||
134 | continue; | |||
135 | ||||
136 | if (st.st_rdev == child_status.ctty | |||
137 | || st.st_rdev == tty_st.st_rdev | |||
138 | || st.st_rdev == console_st.st_rdev) { | |||
139 | if (n == allocated) { | |||
140 | allocated = allocated ? 2 * allocated : 2; | |||
141 | tmp = realloc(fds, allocated * sizeof *tmp); | |||
142 | if (tmp == NULL((void*)0)) { | |||
143 | child->error = errno(*__errno_location ()); | |||
144 | error("Unable to allocate memory for fd array."); | |||
145 | free(fds); | |||
146 | fds = NULL((void*)0); | |||
147 | goto out; | |||
148 | } | |||
149 | fds = tmp; | |||
150 | } | |||
151 | debug("Found an alias for the tty: %s", d->d_name); | |||
152 | fds[n++] = atoi(d->d_name); | |||
153 | } | |||
154 | } | |||
155 | out: | |||
156 | *count = n; | |||
157 | closedir(dir); | |||
158 | return fds; | |||
159 | } | |||
160 | ||||
161 | void move_process_group(struct ptrace_child *child, pid_t from, pid_t to) { | |||
162 | DIR *dir; | |||
163 | struct dirent *d; | |||
164 | pid_t pid; | |||
165 | char *p; | |||
166 | int err; | |||
167 | ||||
168 | if ((dir = opendir("/proc/")) == NULL((void*)0)) | |||
169 | return; | |||
170 | ||||
171 | while ((d = readdir(dir)) != NULL((void*)0)) { | |||
172 | if (d->d_name[0] == '.') continue; | |||
173 | pid = strtol(d->d_name, &p, 10); | |||
174 | if (*p) continue; | |||
175 | if (getpgid(pid) == from) { | |||
176 | debug("Change pgid for pid %d", pid); | |||
177 | err = do_syscall(child, setpgid, pid, to, 0, 0, 0, 0)ptrace_remote_syscall((child), ptrace_syscall_numbers((child) )->nr_setpgid, pid, to, 0, 0, 0, 0); | |||
178 | if (err < 0) | |||
179 | error(" failed: %s", strerror(-err)); | |||
180 | } | |||
181 | } | |||
182 | closedir(dir); | |||
183 | } | |||
184 | ||||
185 | int do_setsid(struct ptrace_child *child) { | |||
186 | int err = 0; | |||
187 | struct ptrace_child dummy; | |||
188 | ||||
189 | err = do_syscall(child, fork, 0, 0, 0, 0, 0, 0)ptrace_remote_syscall((child), ptrace_syscall_numbers((child) )->nr_fork, 0, 0, 0, 0, 0, 0); | |||
190 | if (err < 0) | |||
191 | return err; | |||
192 | ||||
193 | debug("Forked a child: %ld", child->forked_pid); | |||
194 | ||||
195 | err = ptrace_finish_attach(&dummy, child->forked_pid); | |||
196 | if (err < 0) | |||
197 | goto out_kill; | |||
198 | ||||
199 | dummy.state = ptrace_after_syscall; | |||
200 | memcpy(&dummy.user, &child->user, sizeof child->user); | |||
201 | if (ptrace_restore_regs(&dummy)) { | |||
202 | err = dummy.error; | |||
203 | goto out_kill; | |||
204 | } | |||
205 | ||||
206 | err = do_syscall(&dummy, setpgid, 0, 0, 0, 0, 0, 0)ptrace_remote_syscall((&dummy), ptrace_syscall_numbers((& dummy))->nr_setpgid, 0, 0, 0, 0, 0, 0); | |||
207 | if (err < 0) { | |||
208 | error("Failed to setpgid: %s", strerror(-err)); | |||
209 | goto out_kill; | |||
210 | } | |||
211 | ||||
212 | move_process_group(child, child->pid, dummy.pid); | |||
213 | ||||
214 | err = do_syscall(child, setsid, 0, 0, 0, 0, 0, 0)ptrace_remote_syscall((child), ptrace_syscall_numbers((child) )->nr_setsid, 0, 0, 0, 0, 0, 0); | |||
215 | if (err < 0) { | |||
216 | error("Failed to setsid: %s", strerror(-err)); | |||
217 | move_process_group(child, dummy.pid, child->pid); | |||
218 | goto out_kill; | |||
219 | } | |||
220 | ||||
221 | debug("Did setsid()"); | |||
222 | ||||
223 | out_kill: | |||
224 | kill(dummy.pid, SIGKILL9); | |||
225 | ptrace_detach_child(&dummy); | |||
226 | ptrace_wait(&dummy); | |||
227 | do_syscall(child, wait4, dummy.pid, 0, WNOHANG, 0, 0, 0)ptrace_remote_syscall((child), ptrace_syscall_numbers((child) )->nr_wait4, dummy.pid, 0, 1, 0, 0, 0); | |||
228 | return err; | |||
229 | } | |||
230 | ||||
231 | int ignore_hup(struct ptrace_child *child, unsigned long scratch_page) { | |||
232 | int err; | |||
233 | if (ptrace_syscall_numbers(child)->nr_signal != -1) { | |||
234 | err = do_syscall(child, signal, SIGHUP, (unsigned long)SIG_IGN, 0, 0, 0, 0)ptrace_remote_syscall((child), ptrace_syscall_numbers((child) )->nr_signal, 1, (unsigned long)((__sighandler_t) 1), 0, 0 , 0, 0); | |||
235 | } else { | |||
236 | struct sigaction act = { | |||
237 | .sa_handler__sigaction_handler.sa_handler = SIG_IGN((__sighandler_t) 1), | |||
238 | }; | |||
239 | err = ptrace_memcpy_to_child(child, scratch_page, | |||
240 | &act, sizeof act); | |||
241 | if (err < 0) | |||
242 | return err; | |||
243 | err = do_syscall(child, rt_sigaction,ptrace_remote_syscall((child), ptrace_syscall_numbers((child) )->nr_rt_sigaction, 1, scratch_page, 0, 8, 0, 0) | |||
244 | SIGHUP, scratch_page,ptrace_remote_syscall((child), ptrace_syscall_numbers((child) )->nr_rt_sigaction, 1, scratch_page, 0, 8, 0, 0) | |||
245 | 0, 8, 0, 0)ptrace_remote_syscall((child), ptrace_syscall_numbers((child) )->nr_rt_sigaction, 1, scratch_page, 0, 8, 0, 0); | |||
246 | } | |||
247 | return err; | |||
248 | } | |||
249 | ||||
250 | /* | |||
251 | * Wait for the specific pid to enter state 'T', or stopped. We have to pull the | |||
252 | * /proc file rather than attaching with ptrace() and doing a wait() because | |||
253 | * half the point of this exercise is for the process's real parent (the shell) | |||
254 | * to see the TSTP. | |||
255 | * | |||
256 | * In case the process is masking or ignoring SIGTSTP, we time out after a | |||
257 | * second and continue with the attach -- it'll still work mostly right, you | |||
258 | * just won't get the old shell back. | |||
259 | */ | |||
260 | void wait_for_stop(pid_t pid, int fd) { | |||
261 | struct timeval start, now; | |||
262 | struct timespec sleep; | |||
263 | struct proc_stat st; | |||
264 | ||||
265 | gettimeofday(&start, NULL((void*)0)); | |||
266 | while (1) { | |||
267 | gettimeofday(&now, NULL((void*)0)); | |||
268 | if ((now.tv_sec > start.tv_sec && now.tv_usec > start.tv_usec) | |||
269 | || (now.tv_sec - start.tv_sec > 1)) { | |||
270 | error("Timed out waiting for child stop."); | |||
271 | break; | |||
272 | } | |||
273 | /* | |||
274 | * If anything goes wrong reading or parsing the stat node, just give | |||
275 | * up. | |||
276 | */ | |||
277 | if (parse_proc_stat(fd, &st)) | |||
278 | break; | |||
279 | if (st.state == 'T') | |||
280 | break; | |||
281 | ||||
282 | sleep.tv_sec = 0; | |||
283 | sleep.tv_nsec = 10000000; | |||
284 | nanosleep(&sleep, NULL((void*)0)); | |||
285 | } | |||
286 | } | |||
287 | ||||
288 | int copy_tty_state(pid_t pid, const char *pty) { | |||
289 | char buf[PATH_MAX4096]; | |||
290 | int fd, err = EINVAL22; | |||
291 | struct termios tio; | |||
292 | int i; | |||
293 | ||||
294 | for (i = 0; i < 3 && err; i++) { | |||
295 | err = 0; | |||
296 | snprintf(buf, sizeof buf, "/proc/%d/fd/%d", pid, i); | |||
297 | ||||
298 | if ((fd = open(buf, O_RDONLY00)) < 0) { | |||
299 | err = -fd; | |||
300 | continue; | |||
301 | } | |||
302 | ||||
303 | if (!isatty(fd)) { | |||
304 | err = ENOTTY25; | |||
305 | goto retry; | |||
306 | } | |||
307 | ||||
308 | if (tcgetattr(fd, &tio) < 0) { | |||
309 | err = -errno(*__errno_location ()); | |||
310 | } | |||
311 | retry: | |||
312 | close(fd); | |||
313 | } | |||
314 | ||||
315 | if (err) | |||
316 | return err; | |||
317 | ||||
318 | if ((fd = open(pty, O_RDONLY00)) < 0) | |||
319 | return -errno(*__errno_location ()); | |||
320 | ||||
321 | if (tcsetattr(fd, TCSANOW0, &tio) < 0) | |||
322 | err = errno(*__errno_location ()); | |||
323 | close(fd); | |||
324 | return -err; | |||
325 | } | |||
326 | ||||
327 | int check_pgroup(pid_t target) { | |||
328 | pid_t pg; | |||
329 | DIR *dir; | |||
330 | struct dirent *d; | |||
331 | pid_t pid; | |||
332 | char *p; | |||
333 | int err = 0; | |||
334 | struct proc_stat pid_stat; | |||
335 | ||||
336 | debug("Checking for problematic process group members..."); | |||
337 | ||||
338 | pg = getpgid(target); | |||
339 | if (pg < 0) { | |||
340 | error("Unable to get pgid (does process %d exist?)", (int)target); | |||
341 | return pg; | |||
342 | } | |||
343 | ||||
344 | if ((dir = opendir("/proc/")) == NULL((void*)0)) | |||
345 | return errno(*__errno_location ()); | |||
346 | ||||
347 | while ((d = readdir(dir)) != NULL((void*)0)) { | |||
348 | if (d->d_name[0] == '.') continue; | |||
349 | pid = strtol(d->d_name, &p, 10); | |||
350 | if (*p) continue; | |||
351 | if (pid == target) continue; | |||
352 | if (getpgid(pid) == pg) { | |||
353 | /* | |||
354 | * We are actually being somewhat overly-conservative here | |||
355 | * -- if pid is a child of target, and has not yet called | |||
356 | * execve(), reptyr's setpgid() strategy may suffice. That | |||
357 | * is a fairly rare case, and annoying to check for, so | |||
358 | * for now let's just bail out. | |||
359 | */ | |||
360 | if ((err = read_proc_stat(pid, &pid_stat))) { | |||
361 | memcpy(pid_stat.comm, "???", 4); | |||
362 | } | |||
363 | error("Process %d (%.*s) shares %d's process group. Unable to attach.\n" | |||
364 | "(This most commonly means that %d has a suprocesses).", | |||
365 | (int)pid, TASK_COMM_LENGTH16, pid_stat.comm, (int)target, (int)target); | |||
366 | err = EINVAL22; | |||
367 | goto out; | |||
368 | } | |||
369 | } | |||
370 | out: | |||
371 | closedir(dir); | |||
372 | return err; | |||
373 | } | |||
374 | ||||
375 | int attach_child(pid_t pid, const char *pty, int force_stdio) { | |||
376 | struct ptrace_child child; | |||
377 | unsigned long scratch_page = -1; | |||
378 | int *child_tty_fds = NULL((void*)0), n_fds, child_fd, statfd; | |||
379 | int i; | |||
380 | int err = 0; | |||
381 | long page_size = sysconf(_SC_PAGE_SIZE_SC_PAGESIZE); | |||
382 | char stat_path[PATH_MAX4096]; | |||
383 | long mmap_syscall; | |||
384 | ||||
385 | if ((err = check_pgroup(pid))) { | |||
| ||||
386 | return err; | |||
387 | } | |||
388 | ||||
389 | if ((err = copy_tty_state(pid, pty))) { | |||
390 | if (err == ENOTTY25 && !force_stdio) { | |||
391 | error("Target is not connected to a terminal.\n" | |||
392 | " Use -s to force attaching anyways."); | |||
393 | return err; | |||
394 | } | |||
395 | } | |||
396 | ||||
397 | snprintf(stat_path, sizeof stat_path, "/proc/%d/stat", pid); | |||
398 | statfd = open(stat_path, O_RDONLY00); | |||
399 | if (statfd < 0) { | |||
400 | error("Unable to open %s: %s", stat_path, strerror(errno(*__errno_location ()))); | |||
401 | return -statfd; | |||
402 | } | |||
403 | ||||
404 | kill(pid, SIGTSTP20); | |||
405 | wait_for_stop(pid, statfd); | |||
406 | ||||
407 | if (ptrace_attach_child(&child, pid)) { | |||
408 | err = child.error; | |||
409 | goto out_cont; | |||
410 | } | |||
411 | ||||
412 | if (ptrace_advance_to_state(&child, ptrace_at_syscall)) { | |||
413 | err = child.error; | |||
414 | goto out_detach; | |||
415 | } | |||
416 | if (ptrace_save_regs(&child)) { | |||
417 | err = child.error; | |||
418 | goto out_detach; | |||
419 | } | |||
420 | ||||
421 | mmap_syscall = ptrace_syscall_numbers(&child)->nr_mmap2; | |||
422 | if (mmap_syscall == -1) | |||
423 | mmap_syscall = ptrace_syscall_numbers(&child)->nr_mmap; | |||
424 | scratch_page = ptrace_remote_syscall(&child, mmap_syscall, 0, | |||
425 | page_size, PROT_READ0x1|PROT_WRITE0x2, | |||
426 | MAP_ANONYMOUS0x20|MAP_PRIVATE0x02, 0, 0); | |||
427 | ||||
428 | if (scratch_page > (unsigned long)-1000) { | |||
429 | err = -(signed long)scratch_page; | |||
430 | goto out_unmap; | |||
431 | } | |||
432 | ||||
433 | debug("Allocated scratch page: %lx", scratch_page); | |||
434 | ||||
435 | if (force_stdio) { | |||
436 | child_tty_fds = malloc(3 * sizeof(int)); | |||
437 | if (!child_tty_fds) { | |||
438 | err = ENOMEM12; | |||
439 | goto out_unmap; | |||
440 | } | |||
441 | n_fds = 3; | |||
442 | child_tty_fds[0] = 0; | |||
443 | child_tty_fds[1] = 1; | |||
444 | child_tty_fds[2] = 2; | |||
445 | } else { | |||
446 | child_tty_fds = get_child_tty_fds(&child, statfd, &n_fds); | |||
447 | if (!child_tty_fds) { | |||
448 | err = child.error; | |||
449 | goto out_unmap; | |||
450 | } | |||
451 | } | |||
452 | ||||
453 | if (ptrace_memcpy_to_child(&child, scratch_page, pty, strlen(pty)+1)) { | |||
454 | err = child.error; | |||
455 | error("Unable to memcpy the pty path to child."); | |||
456 | goto out_free_fds; | |||
457 | } | |||
458 | ||||
459 | child_fd = do_syscall(&child, open,ptrace_remote_syscall((&child), ptrace_syscall_numbers((& child))->nr_open, scratch_page, 02|0400, 0, 0, 0, 0) | |||
460 | scratch_page, O_RDWR|O_NOCTTY,ptrace_remote_syscall((&child), ptrace_syscall_numbers((& child))->nr_open, scratch_page, 02|0400, 0, 0, 0, 0) | |||
461 | 0, 0, 0, 0)ptrace_remote_syscall((&child), ptrace_syscall_numbers((& child))->nr_open, scratch_page, 02|0400, 0, 0, 0, 0); | |||
462 | if (child_fd < 0) { | |||
463 | err = child_fd; | |||
464 | error("Unable to open the tty in the child."); | |||
465 | goto out_free_fds; | |||
466 | } | |||
467 | ||||
468 | debug("Opened the new tty in the child: %d", child_fd); | |||
469 | ||||
470 | err = ignore_hup(&child, scratch_page); | |||
471 | if (err < 0) | |||
472 | goto out_close; | |||
473 | ||||
474 | err = do_syscall(&child, getsid, 0, 0, 0, 0, 0, 0)ptrace_remote_syscall((&child), ptrace_syscall_numbers((& child))->nr_getsid, 0, 0, 0, 0, 0, 0); | |||
475 | if (err != child.pid) { | |||
476 | debug("Target is not a session leader, attempting to setsid."); | |||
477 | err = do_setsid(&child); | |||
478 | } else { | |||
479 | do_syscall(&child, ioctl, child_tty_fds[0], TIOCNOTTY, 0, 0, 0, 0)ptrace_remote_syscall((&child), ptrace_syscall_numbers((& child))->nr_ioctl, child_tty_fds[0], 0x5422, 0, 0, 0, 0); | |||
480 | } | |||
481 | if (err < 0) | |||
482 | goto out_close; | |||
483 | ||||
484 | err = do_syscall(&child, ioctl, child_fd, TIOCSCTTY, 0, 0, 0, 0)ptrace_remote_syscall((&child), ptrace_syscall_numbers((& child))->nr_ioctl, child_fd, 0x540E, 0, 0, 0, 0); | |||
485 | if (err < 0) { | |||
486 | error("Unable to set controlling terminal."); | |||
487 | goto out_close; | |||
488 | } | |||
489 | ||||
490 | debug("Set the controlling tty"); | |||
491 | ||||
492 | for (i = 0; i < n_fds; i++) | |||
493 | do_syscall(&child, dup2, child_fd, child_tty_fds[i], 0, 0, 0, 0)ptrace_remote_syscall((&child), ptrace_syscall_numbers((& child))->nr_dup2, child_fd, child_tty_fds[i], 0, 0, 0, 0); | |||
494 | ||||
495 | ||||
496 | err = 0; | |||
497 | ||||
498 | out_close: | |||
499 | do_syscall(&child, close, child_fd, 0, 0, 0, 0, 0)ptrace_remote_syscall((&child), ptrace_syscall_numbers((& child))->nr_close, child_fd, 0, 0, 0, 0, 0); | |||
500 | out_free_fds: | |||
501 | free(child_tty_fds); | |||
502 | ||||
503 | out_unmap: | |||
504 | do_unmap(&child, scratch_page, page_size); | |||
505 | ||||
506 | ptrace_restore_regs(&child); | |||
507 | out_detach: | |||
508 | ptrace_detach_child(&child); | |||
509 | ||||
510 | if (err == 0) { | |||
511 | kill(child.pid, SIGSTOP19); | |||
512 | wait_for_stop(child.pid, statfd); | |||
513 | } | |||
514 | kill(child.pid, SIGWINCH28); | |||
515 | out_cont: | |||
516 | kill(child.pid, SIGCONT18); | |||
517 | close(statfd); | |||
518 | ||||
519 | return err < 0 ? -err : err; | |||
520 | } |