summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJerry Jelinek <jerry.jelinek@joyent.com>2016-06-20 15:23:05 +0000
committerJerry Jelinek <jerry.jelinek@joyent.com>2016-06-20 15:23:05 +0000
commit158f4dbcf60701c4d8de07146a38d78ab1b77dfe (patch)
tree93e401ca78fefee30972a0f9b76fc02defd317ec
parent45bb95d9cdaf14d6998c192509a67f6da0617971 (diff)
downloadillumos-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.c8
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_brand.c16
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_misc.c28
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_ptrace.c60
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_brand.h8
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;
/*