aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Baldwin <jhb@FreeBSD.org>2019-07-15 21:48:02 +0000
committerJohn Baldwin <jhb@FreeBSD.org>2019-07-15 21:48:02 +0000
commit32451fb9fc71518dd5cf58a06d217eaa777e0206 (patch)
treea0db97356f121a565695a1690ee9c34645004bbb
parent0cc1098a1a6b8353da73ee579091d399652aa57c (diff)
downloadsrc-32451fb9fc71518dd5cf58a06d217eaa777e0206.tar.gz
src-32451fb9fc71518dd5cf58a06d217eaa777e0206.zip
Add ptrace op PT_GET_SC_RET.
This ptrace operation returns a structure containing the error and return values from the current system call. It is only valid when a thread is stopped during a system call exit (PL_FLAG_SCX is set). The sr_error member holds the error value from the system call. Note that this error value is the native FreeBSD error value that has _not_ been translated to an ABI-specific error value similar to the values logged to ktrace. If sr_error is zero, then the return values of the system call will be set in sr_retval[0] and sr_retval[1]. Reviewed by: kib MFC after: 1 month Sponsored by: DARPA Differential Revision: https://reviews.freebsd.org/D20901
Notes
Notes: svn path=/head/; revision=350017
-rw-r--r--lib/libc/sys/ptrace.248
-rw-r--r--sys/kern/sys_process.c63
-rw-r--r--sys/sys/ptrace.h7
-rw-r--r--tests/sys/kern/ptrace_test.c82
4 files changed, 194 insertions, 6 deletions
diff --git a/lib/libc/sys/ptrace.2 b/lib/libc/sys/ptrace.2
index 5a1763474223..6608c115827c 100644
--- a/lib/libc/sys/ptrace.2
+++ b/lib/libc/sys/ptrace.2
@@ -2,7 +2,7 @@
.\" $NetBSD: ptrace.2,v 1.2 1995/02/27 12:35:37 cgd Exp $
.\"
.\" This file is in the public domain.
-.Dd June 2, 2018
+.Dd July 15, 2019
.Dt PTRACE 2
.Os
.Sh NAME
@@ -664,6 +664,52 @@ member of the
but not more than the
.Fa data
bytes in total are copied.
+.It Dv PT_GET_SC_RET
+Fetch the system call return values on exit from a syscall.
+This request is only valid for threads stopped in a syscall
+exit (the
+.Dv PL_FLAG_SCX
+state).
+The
+.Fa addr
+argument specifies a pointer to a
+.Vt "struct ptrace_sc_ret" ,
+which is defined as follows:
+.Bd -literal
+struct ptrace_sc_ret {
+ register_t sr_retval[2];
+ int sr_error;
+};
+.Ed
+.Pp
+The
+.Fa data
+argument is set to the size of the structure.
+.Pp
+If the system call completed successfully,
+.Va sr_error
+is set to zero and the return values of the system call are saved in
+.Va sr_retval .
+If the system call failed to execute,
+.Va sr_error
+field is set to a positive
+.Xr errno 2
+value.
+If the system call completed in an unusual fashion,
+.Va sr_error
+is set to a negative value:
+.Pp
+.Bl -tag -width Dv EJUSTRETURN -compact
+.It Dv ERESTART
+System call will be restarted.
+.It Dv EJUSTRETURN
+System call completed sucessfully but did not set a return value
+.Po for example,
+.Xr setcontext 2
+and
+.Xr sigreturn 2
+.Pc .
+.El
.It Dv PT_FOLLOW_FORK
This request controls tracing for new child processes of a traced process.
If
diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c
index b9649ee8053a..0444af49e82f 100644
--- a/sys/kern/sys_process.c
+++ b/sys/kern/sys_process.c
@@ -76,6 +76,11 @@ struct ptrace_io_desc32 {
uint32_t piod_len;
};
+struct ptrace_sc_ret32 {
+ uint32_t sr_retval[2];
+ int sr_error;
+};
+
struct ptrace_vm_entry32 {
int pve_entry;
int pve_timestamp;
@@ -518,6 +523,17 @@ ptrace_lwpinfo_to32(const struct ptrace_lwpinfo *pl,
pl32->pl_syscall_code = pl->pl_syscall_code;
pl32->pl_syscall_narg = pl->pl_syscall_narg;
}
+
+static void
+ptrace_sc_ret_to32(const struct ptrace_sc_ret *psr,
+ struct ptrace_sc_ret32 *psr32)
+{
+
+ bzero(psr32, sizeof(*psr32));
+ psr32->sr_retval[0] = psr->sr_retval[0];
+ psr32->sr_retval[1] = psr->sr_retval[1];
+ psr32->sr_error = psr->sr_error;
+}
#endif /* COMPAT_FREEBSD32 */
/*
@@ -580,6 +596,7 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
struct ptrace_vm_entry32 pve32;
#endif
char args[sizeof(td->td_sa.args)];
+ struct ptrace_sc_ret psr;
int ptevents;
} r;
void *addr;
@@ -598,6 +615,7 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
case PT_GET_EVENT_MASK:
case PT_LWPINFO:
case PT_GET_SC_ARGS:
+ case PT_GET_SC_RET:
break;
case PT_GETREGS:
BZERO(&r.reg, sizeof r.reg);
@@ -668,6 +686,10 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
error = copyout(r.args, uap->addr, MIN(uap->data,
sizeof(r.args)));
break;
+ case PT_GET_SC_RET:
+ error = copyout(&r.psr, uap->addr, MIN(uap->data,
+ sizeof(r.psr)));
+ break;
}
return (error);
@@ -719,6 +741,7 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
struct thread *td2 = NULL, *td3;
struct ptrace_io_desc *piod = NULL;
struct ptrace_lwpinfo *pl;
+ struct ptrace_sc_ret *psr;
int error, num, tmp;
int proctree_locked = 0;
lwpid_t tid = 0, *buf;
@@ -726,7 +749,11 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
int wrap32 = 0, safe = 0;
struct ptrace_io_desc32 *piod32 = NULL;
struct ptrace_lwpinfo32 *pl32 = NULL;
- struct ptrace_lwpinfo plr;
+ struct ptrace_sc_ret32 *psr32 = NULL;
+ union {
+ struct ptrace_lwpinfo pl;
+ struct ptrace_sc_ret psr;
+ } r;
#endif
curp = td->td_proc;
@@ -1049,6 +1076,38 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
bcopy(td2->td_sa.args, addr, td2->td_sa.narg *
sizeof(register_t));
break;
+
+ case PT_GET_SC_RET:
+ if ((td2->td_dbgflags & (TDB_SCX)) == 0
+#ifdef COMPAT_FREEBSD32
+ || (wrap32 && !safe)
+#endif
+ ) {
+ error = EINVAL;
+ break;
+ }
+#ifdef COMPAT_FREEBSD32
+ if (wrap32) {
+ psr = &r.psr;
+ psr32 = addr;
+ } else
+#endif
+ psr = addr;
+ bzero(psr, sizeof(*psr));
+ psr->sr_error = td2->td_errno;
+ if (psr->sr_error == 0) {
+ psr->sr_retval[0] = td2->td_retval[0];
+ psr->sr_retval[1] = td2->td_retval[1];
+ }
+#ifdef COMPAT_FREEBSD32
+ if (wrap32)
+ ptrace_sc_ret_to32(psr, psr32);
+#endif
+ CTR4(KTR_PTRACE,
+ "PT_GET_SC_RET: pid %d error %d retval %#lx,%#lx",
+ p->p_pid, psr->sr_error, psr->sr_retval[0],
+ psr->sr_retval[1]);
+ break;
case PT_STEP:
case PT_CONTINUE:
@@ -1335,7 +1394,7 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
}
#ifdef COMPAT_FREEBSD32
if (wrap32) {
- pl = &plr;
+ pl = &r.pl;
pl32 = addr;
} else
#endif
diff --git a/sys/sys/ptrace.h b/sys/sys/ptrace.h
index e41f7a3ef024..1ee42318e57e 100644
--- a/sys/sys/ptrace.h
+++ b/sys/sys/ptrace.h
@@ -72,6 +72,7 @@
#define PT_SET_EVENT_MASK 26 /* set mask of optional events */
#define PT_GET_SC_ARGS 27 /* fetch syscall args */
+#define PT_GET_SC_RET 28 /* fetch syscall results */
#define PT_GETREGS 33 /* get general-purpose registers */
#define PT_SETREGS 34 /* set general-purpose registers */
@@ -155,6 +156,12 @@ struct ptrace_lwpinfo32 {
};
#endif
+/* Argument structure for PT_GET_SC_RET. */
+struct ptrace_sc_ret {
+ register_t sr_retval[2]; /* Only valid if sr_error == 0. */
+ int sr_error;
+};
+
/* Argument structure for PT_VM_ENTRY. */
struct ptrace_vm_entry {
int pve_entry; /* Entry number used for iteration. */
diff --git a/tests/sys/kern/ptrace_test.c b/tests/sys/kern/ptrace_test.c
index 1bacf0750c57..4e4e3babc998 100644
--- a/tests/sys/kern/ptrace_test.c
+++ b/tests/sys/kern/ptrace_test.c
@@ -3905,12 +3905,13 @@ ATF_TC_BODY(ptrace__PT_LWPINFO_stale_siginfo, tc)
}
/*
- * A simple test of PT_GET_SC_ARGS.
+ * A simple test of PT_GET_SC_ARGS and PT_GET_SC_RET.
*/
ATF_TC_WITHOUT_HEAD(ptrace__syscall_args);
ATF_TC_BODY(ptrace__syscall_args, tc)
{
struct ptrace_lwpinfo pl;
+ struct ptrace_sc_ret psr;
pid_t fpid, wpid;
register_t args[2];
int events, status;
@@ -3919,6 +3920,7 @@ ATF_TC_BODY(ptrace__syscall_args, tc)
if (fpid == 0) {
trace_me();
kill(getpid(), 0);
+ close(3);
exit(1);
}
@@ -3930,9 +3932,9 @@ ATF_TC_BODY(ptrace__syscall_args, tc)
/*
* Continue the process ignoring the signal, but enabling
- * syscall entry traps.
+ * syscall traps.
*/
- ATF_REQUIRE(ptrace(PT_TO_SCE, fpid, (caddr_t)1, 0) == 0);
+ ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0);
/*
* The next stop should be the syscall entry from getpid().
@@ -3949,6 +3951,25 @@ ATF_TC_BODY(ptrace__syscall_args, tc)
ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0);
/*
+ * The next stop should be the syscall exit from getpid().
+ */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(wpid == fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCX);
+ ATF_REQUIRE(pl.pl_syscall_code == SYS_getpid);
+
+ ATF_REQUIRE(ptrace(PT_GET_SC_RET, wpid, (caddr_t)&psr,
+ sizeof(psr)) != -1);
+ ATF_REQUIRE(psr.sr_error == 0);
+ ATF_REQUIRE(psr.sr_retval[0] == wpid);
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0);
+
+ /*
* The next stop should be the syscall entry from kill().
*/
wpid = waitpid(fpid, &status, 0);
@@ -3966,6 +3987,61 @@ ATF_TC_BODY(ptrace__syscall_args, tc)
ATF_REQUIRE(args[0] == wpid);
ATF_REQUIRE(args[1] == 0);
+ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0);
+
+ /*
+ * The next stop should be the syscall exit from kill().
+ */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(wpid == fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCX);
+ ATF_REQUIRE(pl.pl_syscall_code == SYS_kill);
+
+ ATF_REQUIRE(ptrace(PT_GET_SC_RET, wpid, (caddr_t)&psr,
+ sizeof(psr)) != -1);
+ ATF_REQUIRE(psr.sr_error == 0);
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0);
+
+ /*
+ * The next stop should be the syscall entry from close().
+ */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(wpid == fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE);
+ ATF_REQUIRE(pl.pl_syscall_code == SYS_close);
+ ATF_REQUIRE(pl.pl_syscall_narg == 1);
+
+ ATF_REQUIRE(ptrace(PT_GET_SC_ARGS, wpid, (caddr_t)args,
+ sizeof(args)) != -1);
+ ATF_REQUIRE(args[0] == 3);
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0);
+
+ /*
+ * The next stop should be the syscall exit from close().
+ */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(wpid == fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCX);
+ ATF_REQUIRE(pl.pl_syscall_code == SYS_close);
+
+ ATF_REQUIRE(ptrace(PT_GET_SC_RET, wpid, (caddr_t)&psr,
+ sizeof(psr)) != -1);
+ ATF_REQUIRE(psr.sr_error == EBADF);
+
/* Disable syscall tracing and continue the child to let it exit. */
ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events,
sizeof(events)) == 0);