static long ptrace_getregset(pid_t pid) { # ifdef ARCH_IOVEC_FOR_GETREGSET /* variable iovec */ ARCH_IOVEC_FOR_GETREGSET.iov_len = sizeof(ARCH_REGS_FOR_GETREGSET); return ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &ARCH_IOVEC_FOR_GETREGSET); # else /* constant iovec */ static struct iovec io = { .iov_base = &ARCH_REGS_FOR_GETREGSET, .iov_len = sizeof(ARCH_REGS_FOR_GETREGSET) }; return ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &io); # endif } # ifndef HAVE_GETREGS_OLD # define ptrace_setregset_or_setregs ptrace_setregset static int ptrace_setregset(pid_t pid) { # ifdef ARCH_IOVEC_FOR_GETREGSET /* variable iovec */ return ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &ARCH_IOVEC_FOR_GETREGSET); # else /* constant iovec */ static struct iovec io = { .iov_base = &ARCH_REGS_FOR_GETREGSET, .iov_len = sizeof(ARCH_REGS_FOR_GETREGSET) }; return ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &io); # endif } # endif /* !HAVE_GETREGS_OLD */ #elif defined ARCH_REGS_FOR_GETREGS # define ptrace_getregset_or_getregs ptrace_getregs static long ptrace_getregs(pid_t pid) { # if defined SPARC || defined SPARC64 /* SPARC systems have the meaning of data and addr reversed */ return ptrace(PTRACE_GETREGS, pid, (void *) &ARCH_REGS_FOR_GETREGS, 0); # else return ptrace(PTRACE_GETREGS, pid, NULL, &ARCH_REGS_FOR_GETREGS); # endif } # ifndef HAVE_GETREGS_OLD # define ptrace_setregset_or_setregs ptrace_setregs static int ptrace_setregs(pid_t pid) { # if defined SPARC || defined SPARC64 /* SPARC systems have the meaning of data and addr reversed */ return ptrace(PTRACE_SETREGS, pid, (void *) &ARCH_REGS_FOR_GETREGS, 0); # else return ptrace(PTRACE_SETREGS, pid, NULL, &ARCH_REGS_FOR_GETREGS); # endif } # endif /* !HAVE_GETREGS_OLD */ #endif /* ARCH_REGS_FOR_GETREGSET || ARCH_REGS_FOR_GETREGS */ static void get_regs(pid_t pid) { #undef USE_GET_SYSCALL_RESULT_REGS #ifdef ptrace_getregset_or_getregs if (get_regs_error != -1) return; # ifdef HAVE_GETREGS_OLD /* * Try PTRACE_GETREGSET/PTRACE_GETREGS first, * fallback to getregs_old. */ static int use_getregs_old; if (use_getregs_old < 0) { get_regs_error = ptrace_getregset_or_getregs(pid); return; } else if (use_getregs_old == 0) { get_regs_error = ptrace_getregset_or_getregs(pid); if (get_regs_error >= 0) { use_getregs_old = -1; return; } if (errno == EPERM || errno == ESRCH) return; use_getregs_old = 1; } get_regs_error = getregs_old(pid); # else /* !HAVE_GETREGS_OLD */ /* Assume that PTRACE_GETREGSET/PTRACE_GETREGS works. */ get_regs_error = ptrace_getregset_or_getregs(pid); # endif /* !HAVE_GETREGS_OLD */ #else /* !ptrace_getregset_or_getregs */ # define USE_GET_SYSCALL_RESULT_REGS 1 # warning get_regs is not implemented for this architecture yet get_regs_error = 0; #endif /* !ptrace_getregset_or_getregs */ } #ifdef ptrace_setregset_or_setregs static int set_regs(pid_t pid) { return ptrace_setregset_or_setregs(pid); } #endif /* ptrace_setregset_or_setregs */ struct sysent_buf { struct tcb *tcp; struct_sysent ent; char buf[sizeof("syscall_%lu") + sizeof(kernel_ulong_t) * 3]; }; static void free_sysent_buf(void *ptr) { struct sysent_buf *s = ptr; s->tcp->s_prev_ent = s->tcp->s_ent = NULL; free(ptr); } /* * Returns: * 0: "ignore this ptrace stop", syscall_entering_decode() should return a "bail * out silently" code. * 1: ok, continue in syscall_entering_decode(). * other: error, syscall_entering_decode() should print error indicator * ("????" etc) and return an appropriate code. */ int get_scno(struct tcb *tcp) { get_regs(tcp->pid); if (get_regs_error) return -1; int rc = arch_get_scno(tcp); if (rc != 1) return rc; if (scno_is_valid(tcp->scno)) { tcp->s_ent = &sysent[tcp->scno]; tcp->qual_flg = qual_flags(tcp->scno); } else { struct sysent_buf *s = xcalloc(1, sizeof(*s)); s->tcp = tcp; s->ent.nargs = MAX_ARGS; s->ent.sen = SEN_printargs; s->ent.sys_func = printargs; s->ent.sys_name = s->buf; sprintf(s->buf, "syscall_%" PRI_klu, shuffle_scno(tcp->scno)); tcp->s_ent = &s->ent; tcp->qual_flg = QUAL_RAW | DEFAULT_QUAL_FLAGS; set_tcb_priv_data(tcp, s, free_sysent_buf); if (debug_flag) error_msg("pid %d invalid syscall %" PRI_kld, tcp->pid, tcp->scno); } return 1; } #ifdef USE_GET_SYSCALL_RESULT_REGS static int get_syscall_result_regs(struct tcb *); #endif /* Returns: * 1: ok, continue in syscall_exiting_trace(). * -1: error, syscall_exiting_trace() should print error indicator * ("????" etc) and bail out. */ static int get_syscall_result(struct tcb *tcp) { #ifdef USE_GET_SYSCALL_RESULT_REGS if (get_syscall_result_regs(tcp)) return -1; #endif tcp->u_error = 0; get_error(tcp, !(tcp->s_ent->sys_flags & SYSCALL_NEVER_FAILS)); return 1; } #include "get_scno.c" #include "set_scno.c" #include "get_syscall_args.c" #ifdef USE_GET_SYSCALL_RESULT_REGS # include "get_syscall_result.c" #endif #include "get_error.c" #include "set_error.c" #ifdef HAVE_GETREGS_OLD # include "getregs_old.c" #endif const char * syscall_name(kernel_ulong_t scno) { #if defined X32_PERSONALITY_NUMBER && defined __X32_SYSCALL_BIT if (current_personality == X32_PERSONALITY_NUMBER) scno &= ~__X32_SYSCALL_BIT; #endif return scno_is_valid(scno) ? sysent[scno].sys_name : NULL; }
/* * There are two different modes of operation: * * - Get buffer size. In this case, the callee sets ifc_buf to NULL, * and the kernel returns the buffer size in ifc_len. * - Get actual data. In this case, the callee specifies the buffer address * in ifc_buf and its size in ifc_len. The kernel fills the buffer with * the data, and its amount is returned in ifc_len. * * Note that, technically, the whole struct ifconf is overwritten, * so ifc_buf could be different on exit, but current ioctl handler * implementation does not touch it. */ static int decode_ifconf(struct tcb *const tcp, const kernel_ulong_t addr) { struct_ifconf *entering_ifc = NULL; struct_ifconf *ifc = entering(tcp) ? malloc(sizeof(*ifc)) : alloca(sizeof(*ifc)); if (exiting(tcp)) { entering_ifc = get_tcb_priv_data(tcp); if (!entering_ifc) { error_msg("decode_ifconf: where is my ifconf?"); return 0; } } if (!ifc || umove(tcp, addr, ifc) < 0) { if (entering(tcp)) { free(ifc); tprints(", "); printaddr(addr); } else { /* * We failed to fetch the structure on exiting syscall, * print whatever was fetched on entering syscall. */ if (!entering_ifc->ifc_buf) print_ifc_len(entering_ifc->ifc_len); tprints(", ifc_buf="); printaddr(ptr_to_kulong(entering_ifc->ifc_buf)); tprints("}"); } return RVAL_DECODED | 1; } if (entering(tcp)) { tprints(", {ifc_len="); if (ifc->ifc_buf) print_ifc_len(ifc->ifc_len); set_tcb_priv_data(tcp, ifc, free); return 1; } /* exiting */ if (entering_ifc->ifc_buf && (entering_ifc->ifc_len != ifc->ifc_len)) tprints(" => "); if (!entering_ifc->ifc_buf || (entering_ifc->ifc_len != ifc->ifc_len)) print_ifc_len(ifc->ifc_len); tprints(", ifc_buf="); if (!entering_ifc->ifc_buf || syserror(tcp)) { printaddr(ptr_to_kulong(entering_ifc->ifc_buf)); if (entering_ifc->ifc_buf != ifc->ifc_buf) { tprints(" => "); printaddr(ptr_to_kulong(ifc->ifc_buf)); } } else { struct_ifreq ifr; print_array(tcp, ptr_to_kulong(ifc->ifc_buf), ifc->ifc_len / sizeof(struct_ifreq), &ifr, sizeof(ifr), umoven_or_printaddr, print_ifconf_ifreq, NULL); } tprints("}"); return RVAL_DECODED | 1; }