| File: | attach.c |
| Location: | line 279, column 22 |
| Description: | The left operand of '==' is a garbage 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 | } |