int steal_cleanup_child(struct steal_pty_state *steal) { if (ptrace_memcpy_to_child(&steal->child, steal->child_scratch, "/dev/null", sizeof("/dev/null"))) { return steal->child.error; } int nullfd = do_syscall(&steal->child, openat, -1, steal->child_scratch, O_RDWR, 0, 0, 0); if (nullfd < 0) { return steal->child.error; } int i; for (i = 0; i < steal->master_fds.n; ++i) { do_dup2(&steal->child, nullfd, steal->master_fds.fds[i]); } do_syscall(&steal->child, close, nullfd, 0, 0, 0, 0, 0); do_syscall(&steal->child, close, steal->child_fd, 0, 0, 0, 0, 0); steal->child_fd = 0; ptrace_restore_regs(&steal->child); ptrace_detach_child(&steal->child); ptrace_wait(&steal->child); return 0; }
int ignore_hup(struct ptrace_child *child, child_addr_t scratch_page) { int err; struct sigaction act = { .sa_handler = SIG_IGN, }; err = ptrace_memcpy_to_child(child, scratch_page, &act, sizeof act); if (err < 0) return err; err = do_syscall(child, rt_sigaction, SIGHUP, (unsigned long)scratch_page, 0, 8, 0, 0); return err; }
int ignore_hup(struct ptrace_child *child, unsigned long scratch_page) { int err; if (ptrace_syscall_numbers(child)->nr_signal != -1) { err = do_syscall(child, signal, SIGHUP, (unsigned long)SIG_IGN, 0, 0, 0, 0); } else { struct sigaction act = { .sa_handler = SIG_IGN, }; err = ptrace_memcpy_to_child(child, scratch_page, &act, sizeof act); if (err < 0) return err; err = do_syscall(child, rt_sigaction, SIGHUP, scratch_page, 0, 8, 0, 0); } return err; }
unsigned long ptrace_socketcall(struct ptrace_child *child, unsigned long scratch, unsigned long socketcall, unsigned long p0, unsigned long p1, unsigned long p2, unsigned long p3, unsigned long p4) { // We assume that socketcall is only used on 32-bit // architectures. If there are any 64-bit architectures that do // socketcall, and we port to them, this will need to change. uint32_t args[] = {p0, p1, p2, p3, p4}; int err; err = ptrace_memcpy_to_child(child, scratch, &args, sizeof args); if (err < 0) return (unsigned long)err; return do_syscall(child, socketcall, socketcall, scratch, 0, 0, 0, 0); }
int setup_steal_socket_child(struct steal_pty_state *steal) { int err; err = do_socketcall(&steal->child, steal->child_scratch + sysconf(_SC_PAGE_SIZE)/2, socket, AF_UNIX, SOCK_DGRAM, 0, 0, 0); if (err < 0) return -err; steal->child_fd = err; debug("Opened fd %d in the child.", steal->child_fd); err = ptrace_memcpy_to_child(&steal->child, steal->child_scratch, &steal->addr_un, sizeof(steal->addr_un)); if (err < 0) return steal->child.error; err = do_socketcall(&steal->child, steal->child_scratch + sysconf(_SC_PAGE_SIZE)/2, connect, steal->child_fd, steal->child_scratch, sizeof(steal->addr_un), 0, 0); if (err < 0) return -err; debug("Connected to the shared socket."); return 0; }
int steal_child_pty(struct steal_pty_state *steal) { struct { struct msghdr msg; unsigned char buf[CMSG_SPACE(sizeof(int))]; } buf = {}; struct cmsghdr *cm; int err; buf.msg.msg_control = buf.buf; buf.msg.msg_controllen = CMSG_SPACE(sizeof(int)); cm = CMSG_FIRSTHDR(&buf.msg); cm->cmsg_level = SOL_SOCKET; cm->cmsg_type = SCM_RIGHTS; cm->cmsg_len = CMSG_LEN(sizeof(int)); memcpy(CMSG_DATA(cm), &steal->master_fds.fds[0], sizeof(int)); buf.msg.msg_controllen = cm->cmsg_len; // Relocate for the child buf.msg.msg_control = (void*)(steal->child_scratch + ((uint8_t*)buf.msg.msg_control - (uint8_t*)&buf)); if (ptrace_memcpy_to_child(&steal->child, steal->child_scratch, &buf, sizeof(buf))) { return steal->child.error; } steal->child.error = 0; err = do_socketcall(&steal->child, steal->child_scratch + sysconf(_SC_PAGE_SIZE)/2, sendmsg, steal->child_fd, steal->child_scratch, MSG_DONTWAIT, 0, 0); if (err < 0) { return steal->child.error ? steal->child.error : -err; } debug("Sent the pty fd, going to receive it."); buf.msg.msg_control = buf.buf; buf.msg.msg_controllen = CMSG_SPACE(sizeof(int)); err = recvmsg(steal->sockfd, &buf.msg, MSG_DONTWAIT); if (err < 0) { error("Error receiving message."); return errno; } debug("Got a message: %d bytes, %ld control", err, (long)buf.msg.msg_controllen); if (buf.msg.msg_controllen < CMSG_LEN(sizeof(int))) { error("No fd received?"); return EINVAL; } memcpy(&steal->ptyfd, CMSG_DATA(cm), sizeof(steal->ptyfd)); debug("Got tty fd: %d", steal->ptyfd); return 0; }
int attach_child(pid_t pid, const char *pty, int force_stdio) { struct ptrace_child child; child_addr_t scratch_page = -1; int *child_tty_fds = NULL, n_fds, child_fd, statfd = -1; int i; int err = 0; long page_size = sysconf(_SC_PAGE_SIZE); #ifdef __linux__ char stat_path[PATH_MAX]; #endif if ((err = check_pgroup(pid))) { return err; } if ((err = preflight_check(pid))) { return err; } debug("Using tty: %s", pty); if ((err = copy_tty_state(pid, pty))) { if (err == ENOTTY && !force_stdio) { error("Target is not connected to a terminal.\n" " Use -s to force attaching anyways."); return err; } } #ifdef __linux__ snprintf(stat_path, sizeof stat_path, "/proc/%d/stat", pid); statfd = open(stat_path, O_RDONLY); if (statfd < 0) { error("Unable to open %s: %s", stat_path, strerror(errno)); return -statfd; } #endif kill(pid, SIGTSTP); wait_for_stop(pid, statfd); if ((err = grab_pid(pid, &child, &scratch_page))) { goto out_cont; } if (force_stdio) { child_tty_fds = malloc(3 * sizeof(int)); if (!child_tty_fds) { err = ENOMEM; goto out_unmap; } n_fds = 3; child_tty_fds[0] = 0; child_tty_fds[1] = 1; child_tty_fds[2] = 2; } else { child_tty_fds = get_child_tty_fds(&child, statfd, &n_fds); if (!child_tty_fds) { err = child.error; goto out_unmap; } } if (ptrace_memcpy_to_child(&child, scratch_page, pty, strlen(pty) + 1)) { err = child.error; error("Unable to memcpy the pty path to child."); goto out_free_fds; } child_fd = do_syscall(&child, openat, -1, scratch_page, O_RDWR | O_NOCTTY, 0, 0, 0); if (child_fd < 0) { err = child_fd; error("Unable to open the tty in the child."); goto out_free_fds; } debug("Opened the new tty in the child: %d", child_fd); err = ignore_hup(&child, scratch_page); if (err < 0) goto out_close; err = do_syscall(&child, getsid, 0, 0, 0, 0, 0, 0); if (err != child.pid) { debug("Target is not a session leader, attempting to setsid."); err = do_setsid(&child); } else { do_syscall(&child, ioctl, child_tty_fds[0], TIOCNOTTY, 0, 0, 0, 0); } if (err < 0) goto out_close; err = do_syscall(&child, ioctl, child_fd, TIOCSCTTY, 1, 0, 0, 0); if (err != 0) { /* Seems to be returning >0 for error */ error("Unable to set controlling terminal: %s", strerror(err)); goto out_close; } debug("Set the controlling tty"); for (i = 0; i < n_fds; i++) { err = do_dup2(&child, child_fd, child_tty_fds[i]); if (err < 0) error("Problem moving child fd number %d to new tty: %s", child_tty_fds[i], strerror(errno)); } err = 0; out_close: do_syscall(&child, close, child_fd, 0, 0, 0, 0, 0); out_free_fds: free(child_tty_fds); out_unmap: do_unmap(&child, scratch_page, page_size); ptrace_restore_regs(&child); ptrace_detach_child(&child); if (err == 0) { kill(child.pid, SIGSTOP); wait_for_stop(child.pid, statfd); } kill(child.pid, SIGWINCH); out_cont: kill(child.pid, SIGCONT); #ifdef __linux__ close(statfd); #endif return err < 0 ? -err : err; }
int attach_child(pid_t pid, const char *pty, int force_stdio) { struct ptrace_child child; unsigned long scratch_page = -1; int *child_tty_fds = NULL, n_fds, child_fd, statfd; int i; int err = 0; long page_size = sysconf(_SC_PAGE_SIZE); char stat_path[PATH_MAX]; long mmap_syscall; if ((err = copy_tty_state(pid, pty))) { if (err == ENOTTY && !force_stdio) { error("Target is not connected to a terminal.\n" " Use -s to force attaching anyways."); return err; } } snprintf(stat_path, sizeof stat_path, "/proc/%d/stat", pid); statfd = open(stat_path, O_RDONLY); if (statfd < 0) { error("Unable to open %s: %s", stat_path, strerror(errno)); return -statfd; } kill(pid, SIGTSTP); wait_for_stop(pid, statfd); if (ptrace_attach_child(&child, pid)) { err = child.error; goto out_cont; } if (ptrace_advance_to_state(&child, ptrace_at_syscall)) { err = child.error; goto out_detach; } if (ptrace_save_regs(&child)) { err = child.error; goto out_detach; } mmap_syscall = ptrace_syscall_numbers(&child)->nr_mmap2; if (mmap_syscall == -1) mmap_syscall = ptrace_syscall_numbers(&child)->nr_mmap; scratch_page = ptrace_remote_syscall(&child, mmap_syscall, 0, page_size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, 0, 0); if (scratch_page > (unsigned long)-1000) { err = -(signed long)scratch_page; goto out_unmap; } debug("Allocated scratch page: %lx", scratch_page); if (force_stdio) { child_tty_fds = malloc(3 * sizeof(int)); if (!child_tty_fds) { err = ENOMEM; goto out_unmap; } n_fds = 3; child_tty_fds[0] = 0; child_tty_fds[1] = 1; child_tty_fds[2] = 2; } else { child_tty_fds = get_child_tty_fds(&child, statfd, &n_fds); if (!child_tty_fds) { err = child.error; goto out_unmap; } } if (ptrace_memcpy_to_child(&child, scratch_page, pty, strlen(pty)+1)) { err = child.error; error("Unable to memcpy the pty path to child."); goto out_free_fds; } child_fd = do_syscall(&child, open, scratch_page, O_RDWR|O_NOCTTY, 0, 0, 0, 0); if (child_fd < 0) { err = child_fd; error("Unable to open the tty in the child."); goto out_free_fds; } debug("Opened the new tty in the child: %d", child_fd); err = ignore_hup(&child, scratch_page); if (err < 0) goto out_close; err = do_syscall(&child, getsid, 0, 0, 0, 0, 0, 0); if (err != child.pid) { debug("Target is not a session leader, attempting to setsid."); err = do_setsid(&child); } else { do_syscall(&child, ioctl, child_tty_fds[0], TIOCNOTTY, 0, 0, 0, 0); } if (err < 0) goto out_close; err = do_syscall(&child, ioctl, child_fd, TIOCSCTTY, 0, 0, 0, 0); if (err < 0) { error("Unable to set controlling terminal."); goto out_close; } debug("Set the controlling tty"); for (i = 0; i < n_fds; i++) do_syscall(&child, dup2, child_fd, child_tty_fds[i], 0, 0, 0, 0); err = 0; out_close: do_syscall(&child, close, child_fd, 0, 0, 0, 0, 0); out_free_fds: free(child_tty_fds); out_unmap: do_unmap(&child, scratch_page, page_size); ptrace_restore_regs(&child); out_detach: ptrace_detach_child(&child); if (err == 0) { kill(child.pid, SIGSTOP); wait_for_stop(child.pid, statfd); } kill(child.pid, SIGWINCH); out_cont: kill(child.pid, SIGCONT); close(statfd); return err < 0 ? -err : err; }