示例#1
0
文件: syscall.c 项目: masatake/strace
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;
}
示例#2
0
/*
 * 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;
}