From c03e8ecb74bd4d16b1fa905327fd67249a0bc087 Mon Sep 17 00:00:00 2001 From: Kris Van Hees Date: Tue, 27 Aug 2013 15:49:50 -0400 Subject: [PATCH] dtrace: fix retrieval of arg5 through arg9 Fix the retrieval of arguments passed on the stack for SDT, USDT, and direct call probes. This commit also adds trivial support for testcases related to this fix. Orabug: 17368166 Signed-off-by: Kris Van Hees --- dtrace/dt_test_dev.c | 13 +++++++- dtrace/dtrace_dif.c | 6 ++-- dtrace/dtrace_isa.c | 75 +++++++++++++------------------------------ dtrace/fasttrap_dev.c | 36 ++++++++++++++++++++- dtrace/sdt_dev.c | 38 +++++++++++++++++++++- dtrace/sdt_mod.c | 2 +- 6 files changed, 110 insertions(+), 60 deletions(-) diff --git a/dtrace/dt_test_dev.c b/dtrace/dt_test_dev.c index c0c9abe4c421..2a5f82973ea8 100644 --- a/dtrace/dt_test_dev.c +++ b/dtrace/dt_test_dev.c @@ -65,8 +65,19 @@ void dt_test_destroy(void *arg, dtrace_id_t id, void *parg) static long dt_test_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { + /* + * Yes, this is not nice. + * Not at all. + * But we're doing it anyway... + */ + void (*dt_test_probe)(dtrace_id_t, uintptr_t, uintptr_t, uintptr_t, + uintptr_t, uintptr_t, uintptr_t, uintptr_t, + uintptr_t, uintptr_t, uintptr_t); + if (enabled) { - dtrace_probe(pid, cmd, arg, 2, 3, 4); + dt_test_probe = (void *)&dtrace_probe; + dt_test_probe(pid, cmd, arg, 2ULL, 3ULL, 4ULL, 5ULL, + 6ULL, 7ULL, 8ULL, 9ULL); return 0; } diff --git a/dtrace/dtrace_dif.c b/dtrace/dtrace_dif.c index c1d56a2b154b..cd30f6fed80b 100644 --- a/dtrace/dtrace_dif.c +++ b/dtrace/dtrace_dif.c @@ -1985,7 +1985,7 @@ static uint64_t dtrace_dif_variable(dtrace_mstate_t *mstate, if (ndx >= sizeof(mstate->dtms_arg) / sizeof(mstate->dtms_arg[0])) { int aframes = - mstate->dtms_probe->dtpr_aframes + 2; + mstate->dtms_probe->dtpr_aframes + 3; dtrace_provider_t *pv; uint64_t val; @@ -2070,7 +2070,7 @@ static uint64_t dtrace_dif_variable(dtrace_mstate_t *mstate, if (!dtrace_priv_kernel(state)) return 0; if (!(mstate->dtms_present & DTRACE_MSTATE_STACKDEPTH)) { - int aframes = mstate->dtms_probe->dtpr_aframes + 2; + int aframes = mstate->dtms_probe->dtpr_aframes + 3; mstate->dtms_stackdepth = dtrace_getstackdepth(aframes); mstate->dtms_present |= DTRACE_MSTATE_STACKDEPTH; @@ -2106,7 +2106,7 @@ static uint64_t dtrace_dif_variable(dtrace_mstate_t *mstate, return 0; if (!(mstate->dtms_present & DTRACE_MSTATE_CALLER)) { - int aframes = mstate->dtms_probe->dtpr_aframes + 2; + int aframes = mstate->dtms_probe->dtpr_aframes + 3; if (!DTRACE_ANCHORED(mstate->dtms_probe)) { /* diff --git a/dtrace/dtrace_isa.c b/dtrace/dtrace_isa.c index 698904f9faa9..189d17058169 100644 --- a/dtrace/dtrace_isa.c +++ b/dtrace/dtrace_isa.c @@ -114,66 +114,35 @@ DTRACE_FUWORD(16) DTRACE_FUWORD(32) DTRACE_FUWORD(64) -struct frame { - struct frame *fr_savfp; - unsigned long fr_savpc; -} __attribute__((packed)); - -static void dtrace_invop_callsite(void) +uint64_t dtrace_getarg(int argno, int aframes) { -} - -uint64_t dtrace_getarg(int arg, int aframes) -{ - struct frame *fp = (struct frame *)dtrace_getfp(); - uintptr_t *stack; - int i; + unsigned long bp; + uint64_t *st; uint64_t val; - int regmap[] = { - REG_RDI, - REG_RSI, - REG_RDX, - REG_RCX, - REG_R8, - REG_R9 - }; - int nreg = sizeof(regmap) / sizeof(regmap[0]) - 1; - - for (i = 1; i <= aframes; i++) { - fp = fp->fr_savfp; - - if (fp->fr_savpc == (uintptr_t)dtrace_invop_callsite) { - /* FIXME */ - - goto load; - } - } - - /* - * We know that we did not get here through a trap to get into the - * dtrace_probe() function, so this was a straight call into it from - * a provider. In that case, we need to shift the argument that we - * are looking for, because the probe ID will be the first argument to - * dtrace_probe(). - */ - arg++; + int i; - if (arg <= nreg) { - /* - * This should not happen. If the argument was passed in a - * register then it should have been, ...passed in a register. - */ - DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); - return 0; - } + asm volatile("movq %%rbp,%0" : "=m"(bp)); - arg -= nreg + 1; + for (i = 0; i < aframes; i++) + bp = *((unsigned long *)bp); - stack = (uintptr_t *)&fp[1]; + ASSERT(argno >= 5); -load: + /* + * The first 5 arguments (arg0 through arg4) are passed in registers + * to dtrace_probe(). The remaining arguments (arg5 through arg9) are + * passed on the stack. + * + * Stack layout: + * bp[0] = pushed bp from caller + * bp[1] = return address + * bp[2] = 6th argument (arg5 -> argno = 5) + * bp[3] = 7th argument (arg6 -> argno = 6) + * ... + */ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); - val = stack[arg]; + st = (uint64_t *)bp; + val = st[2 + (argno - 5)]; DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); return val; diff --git a/dtrace/fasttrap_dev.c b/dtrace/fasttrap_dev.c index 5355d811c77a..833685f6f908 100644 --- a/dtrace/fasttrap_dev.c +++ b/dtrace/fasttrap_dev.c @@ -121,6 +121,8 @@ static int fasttrap_pid_probe(fasttrap_machtp_t *mtp, struct pt_regs *regs) { if (atomic64_read(&tp->ftt_proc->ftpc_acount) == 0) return 0; + this_cpu_core->cpu_dtrace_regs = regs; + for (id = tp->ftt_ids; id != NULL; id = id->fti_next) { fasttrap_probe_t *ftp = id->fti_probe; @@ -139,6 +141,8 @@ static int fasttrap_pid_probe(fasttrap_machtp_t *mtp, struct pt_regs *regs) { } } + this_cpu_core->cpu_dtrace_regs = NULL; + if (is_enabled) regs->ax = 1; @@ -703,7 +707,37 @@ static void fasttrap_pid_getargdesc(void *arg, dtrace_id_t id, void *parg, static uint64_t fasttrap_usdt_getarg(void *arg, dtrace_id_t id, void *parg, int argno, int aframes) { - return 0; /* FIXME */ + struct pt_regs *regs = this_cpu_core->cpu_dtrace_regs; + uint64_t *st; + uint64_t val; + + if (regs == NULL) + return 0; + + switch (argno) { + case 0: + return regs->di; + case 1: + return regs->si; + case 2: + return regs->dx; + case 3: + return regs->cx; + case 4: + return regs->r8; + case 5: + return regs->r9; + } + + ASSERT(argno > 5); + + st = (uint64_t *)regs->sp; + DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); + __copy_from_user_inatomic_nocache(&val, (void *)&st[argno - 6], + sizeof(st[0])); + DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); + + return val; } static void fasttrap_pid_destroy(void *arg, dtrace_id_t id, void *parg) diff --git a/dtrace/sdt_dev.c b/dtrace/sdt_dev.c index 44684b42e475..4868db390505 100644 --- a/dtrace/sdt_dev.c +++ b/dtrace/sdt_dev.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "dtrace.h" #include "dtrace_dev.h" @@ -108,8 +109,13 @@ static uint8_t sdt_invop(struct pt_regs *regs) for (; sdt != NULL; sdt = sdt->sdp_hashnext) { if ((uintptr_t)sdt->sdp_patchpoint == regs->ip) { + this_cpu_core->cpu_dtrace_regs = regs; + dtrace_probe(sdt->sdp_id, regs->di, regs->si, regs->dx, regs->cx, regs->r8); + + this_cpu_core->cpu_dtrace_regs = NULL; + return DTRACE_INVOP_NOP; } } @@ -267,7 +273,37 @@ void sdt_getargdesc(void *arg, dtrace_id_t id, void *parg, uint64_t sdt_getarg(void *arg, dtrace_id_t id, void *parg, int argno, int aframes) { - return 0; + struct pt_regs *regs = this_cpu_core->cpu_dtrace_regs; + uint64_t *st; + uint64_t val; + + if (regs == NULL) + return 0; + + switch (argno) { + case 0: + return regs->di; + case 1: + return regs->si; + case 2: + return regs->dx; + case 3: + return regs->cx; + case 4: + return regs->r8; + case 5: + return regs->r9; + } + + ASSERT(argno > 5); + + st = (uint64_t *)regs->sp; + DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); + __copy_from_user_inatomic_nocache(&val, (void *)&st[argno - 6], + sizeof(st[0])); + DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); + + return val; } void sdt_destroy(void *arg, dtrace_id_t id, void *parg) diff --git a/dtrace/sdt_mod.c b/dtrace/sdt_mod.c index accf402f14f7..643b1539929f 100644 --- a/dtrace/sdt_mod.c +++ b/dtrace/sdt_mod.c @@ -119,7 +119,7 @@ static dtrace_pops_t sdt_pops = { NULL, NULL, sdt_getargdesc, - NULL /* sdt_getarg */, + sdt_getarg, NULL, sdt_destroy, }; -- 2.50.1