From 338746b90e745a16b48ba075ad2924da4f8c92af Mon Sep 17 00:00:00 2001 From: markj Date: Thu, 17 Dec 2015 00:00:27 +0000 Subject: [PATCH] Support an arbitrary number of arguments to DTrace syscall probes. Rather than pushing all eight possible arguments into dtrace_probe()'s stack frame, make the syscall_args struct for the current syscall available via the current thread. Using a custom getargval method for the systrace provider, this allows any syscall argument to be fetched, even in kernels that have modified the maximum number of system call arguments. Sponsored by: EMC / Isilon Storage Division --- sys/cddl/dev/dtrace/dtrace_cddl.h | 3 +- sys/cddl/dev/systrace/systrace.c | 89 ++++++++++++++++++------------- sys/kern/subr_syscall.c | 19 ++----- sys/sys/sysent.h | 19 ++++--- 4 files changed, 68 insertions(+), 62 deletions(-) diff --git a/sys/cddl/dev/dtrace/dtrace_cddl.h b/sys/cddl/dev/dtrace/dtrace_cddl.h index eed32a8a09ab..b8ea17a23a54 100644 --- a/sys/cddl/dev/dtrace/dtrace_cddl.h +++ b/sys/cddl/dev/dtrace/dtrace_cddl.h @@ -83,8 +83,8 @@ typedef struct kdtrace_thread { uintptr_t td_dtrace_regv; #endif u_int64_t td_hrtime; /* Last time on cpu. */ - int td_errno; /* Syscall return value. */ void *td_dtrace_sscr; /* Saved scratch space location. */ + void *td_systrace_args; /* syscall probe arguments. */ } kdtrace_thread_t; /* @@ -110,6 +110,7 @@ typedef struct kdtrace_thread { #define t_dtrace_astpc td_dtrace->td_dtrace_astpc #define t_dtrace_regv td_dtrace->td_dtrace_regv #define t_dtrace_sscr td_dtrace->td_dtrace_sscr +#define t_dtrace_systrace_args td_dtrace->td_systrace_args #define p_dtrace_helpers p_dtrace->p_dtrace_helpers #define p_dtrace_count p_dtrace->p_dtrace_count #define p_dtrace_probes p_dtrace->p_dtrace_probes diff --git a/sys/cddl/dev/systrace/systrace.c b/sys/cddl/dev/systrace/systrace.c index a42d488c5f7a..88e680a755f7 100644 --- a/sys/cddl/dev/systrace/systrace.c +++ b/sys/cddl/dev/systrace/systrace.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -53,9 +54,10 @@ __FBSDID("$FreeBSD$"); #include #include #include -#include -#include +#include + +#include #ifdef LINUX_SYSTRACE #if defined(__amd64__) @@ -138,6 +140,7 @@ static void systrace_unload(void *); static void systrace_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *); +static uint64_t systrace_getargval(void *, dtrace_id_t, void *, int, int); static void systrace_provide(void *, dtrace_probedesc_t *); static void systrace_destroy(void *, dtrace_id_t, void *); static void systrace_enable(void *, dtrace_id_t, void *); @@ -164,16 +167,13 @@ static dtrace_pops_t systrace_pops = { NULL, NULL, systrace_getargdesc, - NULL, + systrace_getargval, NULL, systrace_destroy }; static dtrace_provider_id_t systrace_id; -typedef void (*systrace_dtrace_probe_t)(dtrace_id_t, uintptr_t, uintptr_t, - uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); - #ifdef NATIVE_ABI /* * Probe callback function. @@ -183,48 +183,48 @@ typedef void (*systrace_dtrace_probe_t)(dtrace_id_t, uintptr_t, uintptr_t, * compat syscall from something like Linux. */ static void -systrace_probe(uint32_t id, int sysnum, struct sysent *sysent, void *params, - int ret) +systrace_probe(struct syscall_args *sa, enum systrace_probe_t type, int retval) { - uint64_t uargs[8]; - systrace_dtrace_probe_t probe; - int n_args = 0; + uint64_t uargs[nitems(sa->args)]; + dtrace_id_t id; + int n_args, sysnum; + sysnum = sa->code; memset(uargs, 0, sizeof(uargs)); - /* - * Check if this syscall has an argument conversion function - * registered. - */ - if (params != NULL && sysent->sy_systrace_args_func != NULL) { + if (type == SYSTRACE_ENTRY) { + id = sa->callp->sy_entry; + + if (sa->callp->sy_systrace_args_func != NULL) + /* + * Convert the syscall parameters using the registered + * function. + */ + (*sa->callp->sy_systrace_args_func)(sysnum, sa->args, + uargs, &n_args); + else + /* + * Use the built-in system call argument conversion + * function to translate the syscall structure fields + * into the array of 64-bit values that DTrace expects. + */ + systrace_args(sysnum, sa->args, uargs, &n_args); /* - * Convert the syscall parameters using the registered - * function. + * Save probe arguments now so that we can retrieve them if + * the getargval method is called from further down the stack. */ - (*sysent->sy_systrace_args_func)(sysnum, params, uargs, - &n_args); - } else if (params != NULL) { - /* - * Use the built-in system call argument conversion - * function to translate the syscall structure fields - * into the array of 64-bit values that DTrace - * expects. - */ - systrace_args(sysnum, params, uargs, &n_args); + curthread->t_dtrace_systrace_args = uargs; } else { - /* - * Since params is NULL, this is a 'return' probe. - * Set arg0 and arg1 as the return value of this syscall. - */ - uargs[0] = uargs[1] = ret; + id = sa->callp->sy_return; + + curthread->t_dtrace_systrace_args = NULL; + /* Set arg0 and arg1 as the return value of this syscall. */ + uargs[0] = uargs[1] = retval; } /* Process the probe using the converted argments. */ - probe = (systrace_dtrace_probe_t)dtrace_probe; - probe(id, uargs[0], uargs[1], uargs[2], uargs[3], uargs[4], uargs[5], - uargs[6], uargs[7]); + dtrace_probe(id, uargs[0], uargs[1], uargs[2], uargs[3], uargs[4]); } - #endif static void @@ -244,6 +244,21 @@ systrace_getargdesc(void *arg, dtrace_id_t id, void *parg, desc->dtargd_ndx = DTRACE_ARGNONE; } +static uint64_t +systrace_getargval(void *arg __unused, dtrace_id_t id __unused, + void *parg __unused, int argno, int aframes __unused) +{ + uint64_t *uargs; + + uargs = curthread->t_dtrace_systrace_args; + if (uargs == NULL) + /* This is a return probe. */ + return (0); + if (argno >= nitems(((struct syscall_args *)NULL)->args)) + return (0); + return (uargs[argno]); +} + static void systrace_provide(void *arg, dtrace_probedesc_t *desc) { diff --git a/sys/kern/subr_syscall.c b/sys/kern/subr_syscall.c index c0763262f74b..f7bbdfbadd13 100644 --- a/sys/kern/subr_syscall.c +++ b/sys/kern/subr_syscall.c @@ -126,14 +126,9 @@ syscallenter(struct thread *td, struct syscall_args *sa) goto retval; #ifdef KDTRACE_HOOKS - /* - * If the systrace module has registered it's probe - * callback and if there is a probe active for the - * syscall 'entry', process the probe. - */ + /* Give the syscall:::entry DTrace probe a chance to fire. */ if (systrace_probe_func != NULL && sa->callp->sy_entry != 0) - (*systrace_probe_func)(sa->callp->sy_entry, sa->code, - sa->callp, sa->args, 0); + (*systrace_probe_func)(sa, SYSTRACE_ENTRY, 0); #endif AUDIT_SYSCALL_ENTER(sa->code, td); @@ -145,14 +140,10 @@ syscallenter(struct thread *td, struct syscall_args *sa) td->td_errno = error; #ifdef KDTRACE_HOOKS - /* - * If the systrace module has registered it's probe - * callback and if there is a probe active for the - * syscall 'return', process the probe. - */ + /* Give the syscall:::return DTrace probe a chance to fire. */ if (systrace_probe_func != NULL && sa->callp->sy_return != 0) - (*systrace_probe_func)(sa->callp->sy_return, sa->code, - sa->callp, NULL, (error) ? -1 : td->td_retval[0]); + (*systrace_probe_func)(sa, SYSTRACE_RETURN, + error ? -1 : td->td_retval[0]); #endif syscall_thread_exit(td, sa->callp); } diff --git a/sys/sys/sysent.h b/sys/sys/sysent.h index fe78fb4cad1f..d5b88ebe9ebd 100644 --- a/sys/sys/sysent.h +++ b/sys/sys/sysent.h @@ -38,18 +38,18 @@ struct rlimit; struct sysent; struct thread; struct ksiginfo; +struct syscall_args; + +enum systrace_probe_t { + SYSTRACE_ENTRY, + SYSTRACE_RETURN, +}; typedef int sy_call_t(struct thread *, void *); -/* Used by the machine dependent syscall() code. */ -typedef void (*systrace_probe_func_t)(u_int32_t, int, struct sysent *, void *, - int); - -/* - * Used by loaded syscalls to convert arguments to a DTrace array - * of 64-bit arguments. - */ -typedef void (*systrace_args_func_t)(int, void *, u_int64_t *, int *); +typedef void (*systrace_probe_func_t)(struct syscall_args *, + enum systrace_probe_t, int); +typedef void (*systrace_args_func_t)(int, void *, uint64_t *, int *); extern systrace_probe_func_t systrace_probe_func; @@ -84,7 +84,6 @@ struct sysent { /* system call table */ struct image_params; struct __sigset; -struct syscall_args; struct trapframe; struct vnode;