Split syscall_fixup into enter/exit pair of functions
* syscall.c: Create syscall_fixup_on_sysexit() which is a copy of
syscall_fixup().
(trace_syscall_exiting): Call syscall_fixup_on_sysexit() instead of
syscall_fixup().
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
diff --git a/syscall.c b/syscall.c
index d1f0cf2..521ca10 100644
--- a/syscall.c
+++ b/syscall.c
@@ -1237,7 +1237,7 @@
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 @@
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 @@
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)