示例#1
0
文件: syscall.c 项目: masatake/strace
/* Returns:
 * 0: "bail out".
 * 1: ok.
 * -1: error in one of ptrace ops.
 *
 * If not 0, call syscall_exiting_trace(tcp, res), where res is the return
 *    value. Anyway, call syscall_exiting_finish(tcp) then.
 */
int
syscall_exiting_decode(struct tcb *tcp, struct timeval *ptv)
{
	/* Measure the exit time as early as possible to avoid errors. */
	if ((Tflag || cflag) && !(filtered(tcp) || hide_log(tcp)))
		gettimeofday(ptv, NULL);

#ifdef USE_LIBUNWIND
	if (stack_trace_enabled) {
		if (tcp->s_ent->sys_flags & STACKTRACE_INVALIDATE_CACHE)
			unwind_cache_invalidate(tcp);
	}
#endif

	if (filtered(tcp) || hide_log(tcp))
		return 0;

	get_regs(tcp->pid);
#if SUPPORTED_PERSONALITIES > 1
	update_personality(tcp, tcp->currpers);
#endif
	return get_regs_error ? -1 : get_syscall_result(tcp);
}
示例#2
0
static int
trace_syscall_exiting(struct tcb *tcp)
{
	int sys_res;
	struct timeval tv;
	int res;
	long u_error;

	/* Measure the exit time as early as possible to avoid errors. */
	if (Tflag || cflag)
		gettimeofday(&tv, NULL);

#ifdef USE_LIBUNWIND
	if (stack_trace_enabled) {
		if (tcp->s_ent->sys_flags & STACKTRACE_INVALIDATE_CACHE)
			unwind_cache_invalidate(tcp);
	}
#endif

#if SUPPORTED_PERSONALITIES > 1
	update_personality(tcp, tcp->currpers);
#endif
	res = (get_regs_error ? -1 : get_syscall_result(tcp));
	if (res == 1) {
		if (filtered(tcp) || hide_log_until_execve)
			goto ret;
	}

	if (cflag) {
		count_syscall(tcp, &tv);
		if (cflag == CFLAG_ONLY_STATS) {
			goto ret;
		}
	}

	/* If not in -ff mode, and printing_tcp != tcp,
	 * then the log currently does not end with output
	 * of _our syscall entry_, but with something else.
	 * We need to say which syscall's return is this.
	 *
	 * Forced reprinting via TCB_REPRINT is used only by
	 * "strace -ff -oLOG test/threaded_execve" corner case.
	 * It's the only case when -ff mode needs reprinting.
	 */
	if ((followfork < 2 && printing_tcp != tcp) || (tcp->flags & TCB_REPRINT)) {
		tcp->flags &= ~TCB_REPRINT;
		printleader(tcp);
		if (tcp->qual_flg & UNDEFINED_SCNO)
			tprintf("<... %s resumed> ", undefined_scno_name(tcp));
		else
			tprintf("<... %s resumed> ", tcp->s_ent->sys_name);
	}
	printing_tcp = tcp;

	tcp->s_prev_ent = NULL;
	if (res != 1) {
		/* There was error in one of prior ptrace ops */
		tprints(") ");
		tabto();
		tprints("= ? <unavailable>\n");
		line_ended();
		tcp->flags &= ~TCB_INSYSCALL;
		return res;
	}
	tcp->s_prev_ent = tcp->s_ent;

	sys_res = 0;
	if (tcp->qual_flg & QUAL_RAW) {
		/* sys_res = printargs(tcp); - but it's nop on sysexit */
	} else {
	/* FIXME: not_failing_only (IOW, option -z) is broken:
	 * failure of syscall is known only after syscall return.
	 * Thus we end up with something like this on, say, ENOENT:
	 *     open("doesnt_exist", O_RDONLY <unfinished ...>
	 *     {next syscall decode}
	 * whereas the intended result is that open(...) line
	 * is not shown at all.
	 */
		if (not_failing_only && tcp->u_error)
			goto ret;	/* ignore failed syscalls */
		sys_res = tcp->s_ent->sys_func(tcp);
	}

	tprints(") ");
	tabto();
	u_error = tcp->u_error;
	if (tcp->qual_flg & QUAL_RAW) {
		if (u_error)
			tprintf("= -1 (errno %ld)", u_error);
		else
			tprintf("= %#lx", tcp->u_rval);
	}
	else if (!(sys_res & RVAL_NONE) && u_error) {
		switch (u_error) {
		/* Blocked signals do not interrupt any syscalls.
		 * In this case syscalls don't return ERESTARTfoo codes.
		 *
		 * Deadly signals set to SIG_DFL interrupt syscalls
		 * and kill the process regardless of which of the codes below
		 * is returned by the interrupted syscall.
		 * In some cases, kernel forces a kernel-generated deadly
		 * signal to be unblocked and set to SIG_DFL (and thus cause
		 * death) if it is blocked or SIG_IGNed: for example, SIGSEGV
		 * or SIGILL. (The alternative is to leave process spinning
		 * forever on the faulty instruction - not useful).
		 *
		 * SIG_IGNed signals and non-deadly signals set to SIG_DFL
		 * (for example, SIGCHLD, SIGWINCH) interrupt syscalls,
		 * but kernel will always restart them.
		 */
		case ERESTARTSYS:
			/* Most common type of signal-interrupted syscall exit code.
			 * The system call will be restarted with the same arguments
			 * if SA_RESTART is set; otherwise, it will fail with EINTR.
			 */
			tprints("= ? ERESTARTSYS (To be restarted if SA_RESTART is set)");
			break;
		case ERESTARTNOINTR:
			/* Rare. For example, fork() returns this if interrupted.
			 * SA_RESTART is ignored (assumed set): the restart is unconditional.
			 */
			tprints("= ? ERESTARTNOINTR (To be restarted)");
			break;
		case ERESTARTNOHAND:
			/* pause(), rt_sigsuspend() etc use this code.
			 * SA_RESTART is ignored (assumed not set):
			 * syscall won't restart (will return EINTR instead)
			 * even after signal with SA_RESTART set. However,
			 * after SIG_IGN or SIG_DFL signal it will restart
			 * (thus the name "restart only if has no handler").
			 */
			tprints("= ? ERESTARTNOHAND (To be restarted if no handler)");
			break;
		case ERESTART_RESTARTBLOCK:
			/* Syscalls like nanosleep(), poll() which can't be
			 * restarted with their original arguments use this
			 * code. Kernel will execute restart_syscall() instead,
			 * which changes arguments before restarting syscall.
			 * SA_RESTART is ignored (assumed not set) similarly
			 * to ERESTARTNOHAND. (Kernel can't honor SA_RESTART
			 * since restart data is saved in "restart block"
			 * in task struct, and if signal handler uses a syscall
			 * which in turn saves another such restart block,
			 * old data is lost and restart becomes impossible)
			 */
			tprints("= ? ERESTART_RESTARTBLOCK (Interrupted by signal)");
			break;
		default:
			if ((unsigned long) u_error < nerrnos
			    && errnoent[u_error])
				tprintf("= -1 %s (%s)", errnoent[u_error],
					strerror(u_error));
			else
				tprintf("= -1 ERRNO_%lu (%s)", u_error,
					strerror(u_error));
			break;
		}
		if ((sys_res & RVAL_STR) && tcp->auxstr)
			tprintf(" (%s)", tcp->auxstr);
	}
	else {
		if (sys_res & RVAL_NONE)
			tprints("= ?");
		else {
			switch (sys_res & RVAL_MASK) {
			case RVAL_HEX:
#if SUPPORTED_PERSONALITIES > 1
				if (current_wordsize < sizeof(long))
					tprintf("= %#x",
						(unsigned int) tcp->u_rval);
				else
#endif
					tprintf("= %#lx", tcp->u_rval);
				break;
			case RVAL_OCTAL:
				tprintf("= %#lo", tcp->u_rval);
				break;
			case RVAL_UDECIMAL:
				tprintf("= %lu", tcp->u_rval);
				break;
			case RVAL_DECIMAL:
				tprintf("= %ld", tcp->u_rval);
				break;
			case RVAL_FD:
				if (show_fd_path) {
					tprints("= ");
					printfd(tcp, tcp->u_rval);
				}
				else
					tprintf("= %ld", tcp->u_rval);
				break;
#if defined(LINUX_MIPSN32) || defined(X32)
			/*
			case RVAL_LHEX:
				tprintf("= %#llx", tcp->u_lrval);
				break;
			case RVAL_LOCTAL:
				tprintf("= %#llo", tcp->u_lrval);
				break;
			*/
			case RVAL_LUDECIMAL:
				tprintf("= %llu", tcp->u_lrval);
				break;
			/*
			case RVAL_LDECIMAL:
				tprintf("= %lld", tcp->u_lrval);
				break;
			*/
#endif
			default:
				fprintf(stderr,
					"invalid rval format\n");
				break;
			}
		}
		if ((sys_res & RVAL_STR) && tcp->auxstr)
			tprintf(" (%s)", tcp->auxstr);
	}
	if (Tflag) {
		tv_sub(&tv, &tv, &tcp->etime);
		tprintf(" <%ld.%06ld>",
			(long) tv.tv_sec, (long) tv.tv_usec);
	}
	tprints("\n");
	dumpio(tcp);
	line_ended();

#ifdef USE_LIBUNWIND
	if (stack_trace_enabled)
		unwind_print_stacktrace(tcp);
#endif

 ret:
	tcp->flags &= ~TCB_INSYSCALL;
	return 0;
}
示例#3
0
/* Return codes: 1 - ok, 0 - ignore, other - error. */
static int
arch_get_scno(struct tcb *tcp)
{
	kernel_ulong_t scno = 0;
	unsigned int currpers;

#ifndef __X32_SYSCALL_BIT
# define __X32_SYSCALL_BIT	0x40000000
#endif

#if 1
	/*
	 * GETREGSET of NT_PRSTATUS tells us regset size,
	 * which unambiguously detects i386.
	 *
	 * Linux kernel distinguishes x86-64 and x32 processes
	 * solely by looking at __X32_SYSCALL_BIT:
	 * arch/x86/include/asm/compat.h::is_x32_task():
	 * if (task_pt_regs(current)->orig_ax & __X32_SYSCALL_BIT)
	 *	return true;
	 */
	if (x86_io.iov_len == sizeof(i386_regs)) {
		scno = i386_regs.orig_eax;
		currpers = 1;
	} else {
		scno = x86_64_regs.orig_rax;
		currpers = 0;
		if (scno & __X32_SYSCALL_BIT) {
			/*
			 * Syscall number -1 requires special treatment:
			 * it might be a side effect of SECCOMP_RET_ERRNO
			 * filtering that sets orig_rax to -1
			 * in some versions of linux kernel.
			 * If that is the case, then
			 * __X32_SYSCALL_BIT logic does not apply.
			 */
			if ((long long) x86_64_regs.orig_rax != -1) {
				scno -= __X32_SYSCALL_BIT;
				currpers = 2;
			} else {
# ifdef X32
				currpers = 2;
# endif
			}
		}
	}

#elif 0
	/*
	 * cs = 0x33 for long mode (native 64 bit and x32)
	 * cs = 0x23 for compatibility mode (32 bit)
	 * ds = 0x2b for x32 mode (x86-64 in 32 bit)
	 */
	scno = x86_64_regs.orig_rax;
	switch (x86_64_regs.cs) {
		case 0x23:
			currpers = 1;
			break;
		case 0x33:
			if (x86_64_regs.ds == 0x2b) {
				currpers = 2;
				scno &= ~__X32_SYSCALL_BIT;
			} else
				currpers = 0;
			break;
		default:
			error_msg("Unknown value CS=0x%08X while "
				  "detecting personality of process PID=%d",
				  (int)x86_64_regs.cs, tcp->pid);
			currpers = current_personality;
			break;
	}
#elif 0
	/*
	 * This version analyzes the opcode of a syscall instruction.
	 * (int 0x80 on i386 vs. syscall on x86-64)
	 * It works, but is too complicated, and strictly speaking, unreliable.
	 */
	unsigned long call, rip = x86_64_regs.rip;
	/* sizeof(syscall) == sizeof(int 0x80) == 2 */
	rip -= 2;
	errno = 0;
	call = ptrace(PTRACE_PEEKTEXT, tcp->pid, (char *)rip, (char *)0);
	if (errno)
		perror_msg("ptrace_peektext failed");
	switch (call & 0xffff) {
		/* x86-64: syscall = 0x0f 0x05 */
		case 0x050f:
			currpers = 0;
			break;
		/* i386: int 0x80 = 0xcd 0x80 */
		case 0x80cd:
			currpers = 1;
			break;
		default:
			currpers = current_personality;
			error_msg("Unknown syscall opcode (0x%04X) while "
				  "detecting personality of process PID=%d",
				  (int)call, tcp->pid);
			break;
	}
#endif

#ifdef X32
	/*
	 * If we are built for a x32 system, then personality 0 is x32
	 * (not x86_64), and stracing of x86_64 apps is not supported.
	 * Stracing of i386 apps is still supported.
	 */
	if (currpers == 0) {
		error_msg("syscall_%" PRI_klu "(...) in unsupported "
			  "64-bit mode of process PID=%d", scno, tcp->pid);
		return 0;
	}
	currpers &= ~2; /* map 2,1 to 0,1 */
#endif /* X32 */

	update_personality(tcp, currpers);
	tcp->scno = scno;
	return 1;
}