diff --git a/syscall.c b/syscall.c index d1f0cf22..521ca10b 100644 --- a/syscall.c +++ b/syscall.c @@ -1237,7 +1237,7 @@ known_scno(struct tcb *tcp) return scno; } -/* Called in trace_syscall() at each syscall entry and exit. +/* Called at each syscall entry. * Returns: * 0: "ignore this ptrace stop", bail out of trace_syscall() silently. * 1: ok, continue in trace_syscall(). @@ -1987,6 +1987,185 @@ get_syscall_result(struct tcb *tcp) return 1; } +/* Called at each syscall exit. + * Returns: + * 0: "ignore this ptrace stop", bail out of trace_syscall() silently. + * 1: ok, continue in trace_syscall(). + * other: error, trace_syscall() should print error indicator + * ("????" etc) and bail out. + */ +static int +syscall_fixup_on_sysexit(struct tcb *tcp) +{ +#ifdef USE_PROCFS + int scno = known_scno(tcp); + + if (entering(tcp)) { + if (tcp->status.PR_WHY != PR_SYSENTRY) { + if ( + scno == SYS_fork +#ifdef SYS_vfork + || scno == SYS_vfork +#endif /* SYS_vfork */ +#ifdef SYS_fork1 + || scno == SYS_fork1 +#endif /* SYS_fork1 */ +#ifdef SYS_forkall + || scno == SYS_forkall +#endif /* SYS_forkall */ +#ifdef SYS_rfork1 + || scno == SYS_rfork1 +#endif /* SYS_fork1 */ +#ifdef SYS_rforkall + || scno == SYS_rforkall +#endif /* SYS_rforkall */ + ) { + /* We are returning in the child, fake it. */ + tcp->status.PR_WHY = PR_SYSENTRY; + trace_syscall(tcp); + tcp->status.PR_WHY = PR_SYSEXIT; + } + else { + fprintf(stderr, "syscall: missing entry\n"); + tcp->flags |= TCB_INSYSCALL; + } + } + } + else { + if (tcp->status.PR_WHY != PR_SYSEXIT) { + fprintf(stderr, "syscall: missing exit\n"); + tcp->flags &= ~TCB_INSYSCALL; + } + } +#endif /* USE_PROCFS */ + +#ifdef SUNOS4 + if (entering(tcp)) { + if (scno == 0) { + fprintf(stderr, "syscall: missing entry\n"); + tcp->flags |= TCB_INSYSCALL; + } + } + else { + if (scno != 0) { + if (debug) { + /* + * This happens when a signal handler + * for a signal which interrupted a + * a system call makes another system call. + */ + fprintf(stderr, "syscall: missing exit\n"); + } + tcp->flags &= ~TCB_INSYSCALL; + } + } +#endif /* SUNOS4 */ + +#ifdef LINUX + /* A common case of "not a syscall entry" is post-execve SIGTRAP */ +#if defined (I386) + /* With PTRACE_O_TRACEEXEC, post-execve SIGTRAP is disabled. + * Every extra ptrace call is expensive, so check EAX + * on syscall entry only if PTRACE_O_TRACEEXEC is not enabled: + */ + if (entering(tcp) && !(ptrace_setoptions & PTRACE_O_TRACEEXEC)) { + if (upeek(tcp, 4*EAX, &eax) < 0) + return -1; + if (eax != -ENOSYS) { + if (debug) + fprintf(stderr, "not a syscall entry (eax = %ld)\n", eax); + return 0; + } + } +#elif defined (X86_64) + if (entering(tcp) && !(ptrace_setoptions & PTRACE_O_TRACEEXEC)) { + if (upeek(tcp, 8*RAX, &rax) < 0) + return -1; + if (current_personality == 1) + rax = (long int)(int)rax; /* sign extend from 32 bits */ + if (rax != -ENOSYS && entering(tcp)) { + if (debug) + fprintf(stderr, "not a syscall entry (rax = %ld)\n", rax); + return 0; + } + } +#elif defined (S390) || defined (S390X) + if (upeek(tcp, PT_GPR2, &gpr2) < 0) + return -1; + if (syscall_mode != -ENOSYS) + syscall_mode = tcp->scno; + if (gpr2 != syscall_mode && entering(tcp)) { + if (debug) + fprintf(stderr, "not a syscall entry (gpr2 = %ld)\n", gpr2); + return 0; + } + else if (((tcp->flags & (TCB_INSYSCALL|TCB_WAITEXECVE)) + == (TCB_INSYSCALL|TCB_WAITEXECVE)) + && (gpr2 == -ENOSYS || gpr2 == tcp->scno)) { + /* + * Return from execve. + * Fake a return value of zero. We leave the TCB_WAITEXECVE + * flag set for the post-execve SIGTRAP to see and reset. + */ + gpr2 = 0; + } +#elif defined (POWERPC) +# define SO_MASK 0x10000000 + if (upeek(tcp, sizeof(unsigned long)*PT_CCR, &flags) < 0) + return -1; + if (upeek(tcp, sizeof(unsigned long)*PT_R3, &result) < 0) + return -1; + if (flags & SO_MASK) + result = -result; +#elif defined (M68K) + if (upeek(tcp, 4*PT_D0, &d0) < 0) + return -1; + if (d0 != -ENOSYS && entering(tcp)) { + if (debug) + fprintf(stderr, "not a syscall entry (d0 = %ld)\n", d0); + return 0; + } +#elif defined (ARM) + /* + * Nothing required + */ +#elif defined(BFIN) + if (upeek(tcp, PT_R0, &r0) < 0) + return -1; +#elif defined (HPPA) + if (upeek(tcp, PT_GR28, &r28) < 0) + return -1; +#elif defined(IA64) + if (upeek(tcp, PT_R10, &r10) < 0) + return -1; + if (upeek(tcp, PT_R8, &r8) < 0) + return -1; + if (ia32 && r8 != -ENOSYS && entering(tcp)) { + if (debug) + fprintf(stderr, "not a syscall entry (r8 = %ld)\n", r8); + return 0; + } +#elif defined(CRISV10) || defined(CRISV32) + if (upeek(tcp, 4*PT_R10, &r10) < 0) + return -1; + if (r10 != -ENOSYS && entering(tcp)) { + if (debug) + fprintf(stderr, "not a syscall entry (r10 = %ld)\n", r10); + return 0; + } +#elif defined(MICROBLAZE) + if (upeek(tcp, 3 * 4, &r3) < 0) + return -1; + if (r3 != -ENOSYS && entering(tcp)) { + if (debug) + fprintf(stderr, "not a syscall entry (r3 = %ld)\n", r3); + return 0; + } +#endif +#endif /* LINUX */ + return 1; +} + #ifdef LINUX /* * Check the syscall return value register value for whether it is @@ -2302,7 +2481,7 @@ trace_syscall_exiting(struct tcb *tcp) if (res == 0) return res; if (res == 1) - res = syscall_fixup(tcp); + res = syscall_fixup_on_sysexit(tcp); if (res == 0) return res; if (res == 1)