diff options
author | Jerry Jelinek <jerry.jelinek@joyent.com> | 2016-06-20 15:23:05 +0000 |
---|---|---|
committer | Jerry Jelinek <jerry.jelinek@joyent.com> | 2016-06-20 15:23:05 +0000 |
commit | 158f4dbcf60701c4d8de07146a38d78ab1b77dfe (patch) | |
tree | 93e401ca78fefee30972a0f9b76fc02defd317ec | |
parent | 45bb95d9cdaf14d6998c192509a67f6da0617971 (diff) | |
download | illumos-joyent-158f4dbcf60701c4d8de07146a38d78ab1b77dfe.tar.gz |
OS-5431 under many conditions, ptrace (strace) on Ubuntu 16 shows syscalls return ENOSYS
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
-rw-r--r-- | usr/src/lib/brand/lx/lx_brand/common/signal.c | 8 | ||||
-rw-r--r-- | usr/src/uts/common/brand/lx/os/lx_brand.c | 16 | ||||
-rw-r--r-- | usr/src/uts/common/brand/lx/os/lx_misc.c | 28 | ||||
-rw-r--r-- | usr/src/uts/common/brand/lx/os/lx_ptrace.c | 60 | ||||
-rw-r--r-- | usr/src/uts/common/brand/lx/sys/lx_brand.h | 8 |
5 files changed, 109 insertions, 11 deletions
diff --git a/usr/src/lib/brand/lx/lx_brand/common/signal.c b/usr/src/lib/brand/lx/lx_brand/common/signal.c index 80f1bf09a1..7d07dfe66d 100644 --- a/usr/src/lib/brand/lx/lx_brand/common/signal.c +++ b/usr/src/lib/brand/lx/lx_brand/common/signal.c @@ -25,7 +25,7 @@ */ /* - * Copyright 2015 Joyent, Inc. All rights reserved. + * Copyright 2016 Joyent, Inc. All rights reserved. */ #include <sys/types.h> @@ -1077,6 +1077,12 @@ lx_rt_sigreturn(void) ucontext_t *retucp; uintptr_t sp; + /* + * Since we don't take the normal return path from this syscall, we + * inform the kernel that we're returning, for the sake of ptrace. + */ + (void) syscall(SYS_brand, B_PTRACE_SIG_RETURN); + /* Get the registers at the emulated Linux rt_sigreturn syscall */ ucp = lx_syscall_regs(); diff --git a/usr/src/uts/common/brand/lx/os/lx_brand.c b/usr/src/uts/common/brand/lx/os/lx_brand.c index c74d5e8ebd..71290b0bdc 100644 --- a/usr/src/uts/common/brand/lx/os/lx_brand.c +++ b/usr/src/uts/common/brand/lx/os/lx_brand.c @@ -1485,6 +1485,22 @@ lx_brandsys(int cmd, int64_t *rval, uintptr_t arg1, uintptr_t arg2, return (lx_ptrace_set_clone_inherit((int)arg1, arg2 == 0 ? B_FALSE : B_TRUE)); + case B_PTRACE_SIG_RETURN: { + /* + * Our ptrace emulation must emit PR_SYSEXIT for rt_sigreturn. + * Since that syscall does not pass through the normal + * emulation, which would call lx_syscall_return, the event is + * emitted manually. A successful result of the syscall is + * assumed since there is little to be done in the face of + * failure. + */ + struct regs *rp = lwptoregs(lwp); + + rp->r_r0 = 0; + lx_ptrace_stop(LX_PR_SYSEXIT); + return (0); + } + case B_UNSUPPORTED: { char dmsg[256]; diff --git a/usr/src/uts/common/brand/lx/os/lx_misc.c b/usr/src/uts/common/brand/lx/os/lx_misc.c index 267c720e67..5932495655 100644 --- a/usr/src/uts/common/brand/lx/os/lx_misc.c +++ b/usr/src/uts/common/brand/lx/os/lx_misc.c @@ -550,6 +550,11 @@ lx_forklwp(klwp_t *srclwp, klwp_t *dstlwp) */ dst->br_lwp_flags = src->br_lwp_flags & BR_CPU_BOUND; dst->br_scall_args = NULL; + + /* + * Flag so child doesn't ptrace-stop on syscall exit. + */ + dst->br_ptrace_flags |= LX_PTF_NOSTOP; } /* @@ -1078,10 +1083,27 @@ lx_regs_location(lx_lwp_data_t *lwpd, void **ucp, boolean_t for_write) /* setting registers not allowed in this state */ break; } - if (lwpd->br_ptrace_whystop == PR_BRAND && - lwpd->br_ptrace_whatstop == LX_PR_EVENT) { + if (lwpd->br_ptrace_whystop == PR_BRAND) { /* Called while ptrace-event-stopped by lx_exec. */ - return (LX_REG_LOC_LWP); + if (lwpd->br_ptrace_whatstop == LX_PR_EVENT) { + return (LX_REG_LOC_LWP); + } + + /* Called while ptrace-event-stopped after clone. */ + if (lwpd->br_ptrace_whatstop == LX_PR_SIGNALLED && + lwpd->br_ptrace_stopsig == LX_SIGSTOP && + (lwpd->br_ptrace_flags & LX_PTF_STOPPED)) { + return (LX_REG_LOC_LWP); + } + + /* + * Called to obtain syscall exit for other cases + * (e.g. pseudo return from rt_sigreturn). + */ + if (lwpd->br_ptrace_whatstop == LX_PR_SYSEXIT && + (lwpd->br_ptrace_flags & LX_PTF_STOPPED)) { + return (LX_REG_LOC_LWP); + } } break; default: diff --git a/usr/src/uts/common/brand/lx/os/lx_ptrace.c b/usr/src/uts/common/brand/lx/os/lx_ptrace.c index 0f521df61b..e619149bb8 100644 --- a/usr/src/uts/common/brand/lx/os/lx_ptrace.c +++ b/usr/src/uts/common/brand/lx/os/lx_ptrace.c @@ -127,10 +127,10 @@ * * Various actions, either directly ptrace(2) related or commonly associated * with tracing, cause process- or thread-directed SIGSTOP signals to be sent - * to tracees. These signals, and indeed any signal other than SIGKILL, can - * be suppressed by the tracer when using a restarting request (including - * PTRACE_DETACH) on a child. The signal may also be substituted for a - * different signal. + * to tracees (a "signal-delivery-stop"). These signals, and indeed any signal + * other than SIGKILL, can be suppressed by the tracer when using a restarting + * request (including PTRACE_DETACH) on a child. The signal may also be + * substituted for a different signal. * * If a SIGSTOP (or other stopping signal) is not suppressed by the tracer, * it will induce the regular illumos native job control stop of the entire @@ -148,6 +148,20 @@ * cleared after each stop; for ongoing system call tracing the tracee must * be continuously restarted with PTRACE_SYSCALL. * + * SPECIAL CASES FOR STOP EVENTS + * + * The strace command is one of the primary consumers of ptrace. In order for + * strace to properly understand what is actually happening when it receives a + * signal associated with a stop event, these signals must match Linux behavior + * exactly or the strace consumer will get out of sync and report incorrect + * state. There are a couple of special cases we have to handle to provide + * proper interaction of the syscall-entry-stop, syscall-exit-stop, and + * signal-delivery-stop events: + * 1) The child process of a clone/fork does not emit a syscall-exit-stop event. + * 2) A signal that arrives between syscall-enter-stop & syscall-exit-stop must + * not immediately emit signal-delivery-stop. This event must be emitted + * after the syscall is interrupted and syscall-exit-stop has been emitted. + * * EVENT STOPS * * Various events (particularly FORK, VFORK, CLONE, EXEC and EXIT) are @@ -1042,6 +1056,9 @@ lx_ptrace_attach(pid_t lx_pid) rlwpd->br_ptrace_attach = LX_PTA_ATTACH; rlwpd->br_ptrace_tracer = accord; + /* Don't emit ptrace syscall-stop-exit event on kernel exit. */ + rlwpd->br_ptrace_flags |= LX_PTF_NOSTOP; + /* * We had no tracer, and are thus not in the tracees list. * It is safe to take the tracee list lock while we insert @@ -1568,6 +1585,16 @@ lx_ptrace_stop(ushort_t what) * Lock this process and re-check the condition. */ mutex_enter(&p->p_lock); + + /* + * The child after a fork/clone doesn't emit syscall-exit-stop event. + */ + if (what == LX_PR_SYSEXIT && (lwpd->br_ptrace_flags & LX_PTF_NOSTOP)) { + lwpd->br_ptrace_flags &= ~LX_PTF_NOSTOP; + mutex_exit(&p->p_lock); + return (B_FALSE); + } + if (lwpd->br_ptrace_tracer == NULL) { VERIFY0(lwpd->br_ptrace_flags & LX_PTF_SYSCALL); mutex_exit(&p->p_lock); @@ -1575,6 +1602,12 @@ lx_ptrace_stop(ushort_t what) } if (what == LX_PR_SYSENTRY || what == LX_PR_SYSEXIT) { + if (what == LX_PR_SYSENTRY) { + lwpd->br_ptrace_flags |= LX_PTF_INSYSCALL; + } else { + lwpd->br_ptrace_flags &= ~LX_PTF_INSYSCALL; + } + /* * This is a syscall-entry-stop or syscall-exit-stop point. */ @@ -1642,6 +1675,17 @@ lx_ptrace_issig_stop(proc_t *p, klwp_t *lwp) } /* + * We can't deliver the signal-delivery-stop condition while we're + * between the syscall-enter-stop and syscall-exit-stop conditions. + * We must first let the signal interrupt the in-progress syscall, let + * it emit syscall-exit-stop with the interrupted result, then we'll + * come back here to emit signal-delivery-stop. + */ + if (lwpd->br_ptrace_flags & LX_PTF_INSYSCALL) { + return (0); + } + + /* * We stash the signal on the LWP where our waitid_helper will find it * and enter the ptrace "signal-delivery-stop" condition. */ @@ -2401,6 +2445,14 @@ lx_ptrace_kernel(int ptrace_op, pid_t lxpid, uintptr_t addr, uintptr_t data) return (ESRCH); } + if (ptrace_op == LX_PTRACE_DETACH) { + /* + * We're detaching, make sure in-syscall flag is off so that + * signal will stop the process directly. + */ + remote->br_ptrace_flags &= ~LX_PTF_INSYSCALL; + } + /* * Attempt to lock the target LWP. */ diff --git a/usr/src/uts/common/brand/lx/sys/lx_brand.h b/usr/src/uts/common/brand/lx/sys/lx_brand.h index 88546dbe80..01b74f727e 100644 --- a/usr/src/uts/common/brand/lx/sys/lx_brand.h +++ b/usr/src/uts/common/brand/lx/sys/lx_brand.h @@ -108,7 +108,7 @@ extern "C" { #define B_SET_NATIVE_STACK 147 #define B_SIGEV_THREAD_ID 148 #define B_OVERRIDE_KERN_VER 149 -/* formerly B_NOTIFY_VDSO_LOC 150 */ +#define B_PTRACE_SIG_RETURN 150 #define B_GET_PERSONALITY 151 #ifndef _ASM @@ -379,7 +379,7 @@ typedef enum lx_accord_flags { * Flags values for "br_ptrace_flags" in the LWP-specific data. */ typedef enum lx_ptrace_flags { - LX_PTF_SYSCALL = 0x01, + LX_PTF_SYSCALL = 0x01, /* handling syscall or a trap */ LX_PTF_EXITING = 0x02, LX_PTF_STOPPING = 0x04, LX_PTF_INHERIT = 0x08, @@ -387,7 +387,9 @@ typedef enum lx_ptrace_flags { LX_PTF_PARENT_WAIT = 0x20, LX_PTF_CLDPEND = 0x40, LX_PTF_CLONING = 0x80, - LX_PTF_WAITPEND = 0x100 + LX_PTF_WAITPEND = 0x100, + LX_PTF_NOSTOP = 0x200, /* disable syscall stop event */ + LX_PTF_INSYSCALL = 0x400 /* between syscall enter & exit */ } lx_ptrace_flags_t; /* |