Index: cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c =================================================================== --- cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c +++ cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c @@ -242,6 +242,8 @@ { "copyoutstr", DT_IDENT_FUNC, 0, DIF_SUBR_COPYOUTSTR, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(char *, uintptr_t, size_t)" }, +{ "copyoutmbuf", DT_IDENT_FUNC, 0, DIF_SUBR_COPYOUTMBUF, DT_ATTR_STABCMN, DT_VERS_1_0, + &dt_idops_func, "void *(struct mbuf *, size_t, [size_t])" }, { "count", DT_IDENT_AGGFUNC, 0, DTRACEAGG_COUNT, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void()" }, { "curthread", DT_IDENT_SCALAR, 0, DIF_VAR_CURTHREAD, Index: cddl/contrib/opensolaris/lib/libdtrace/common/mknames.sh =================================================================== --- cddl/contrib/opensolaris/lib/libdtrace/common/mknames.sh +++ cddl/contrib/opensolaris/lib/libdtrace/common/mknames.sh @@ -45,7 +45,7 @@ switch (subr) {" nawk ' -/^#define[ ]*DIF_SUBR_/ && $2 != "DIF_SUBR_MAX" { +/^#define[ ]*DIF_SUBR_/ && $2 != "DIF_SUBR_MAX" && $2 != "DIF_SUBR_FBSD_MIN" && $2 != "DIF_SUBR_FBSD_MAX"{ printf("\tcase %s: return (\"%s\");\n", $2, tolower(substr($2, 10))); }' Index: sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c +++ sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c @@ -121,6 +121,7 @@ #include #include #include +#include #include #include #include @@ -607,6 +608,10 @@ static int dtrace_canstore_remains(uint64_t, size_t, size_t *, dtrace_mstate_t *, dtrace_vstate_t *); +#ifdef __FreeBSD__ +void dtrace_mbuf_copydata(const struct mbuf *m, int off, int len, uintptr_t cp); +#endif /* __FreeBSD__ */ + /* * DTrace Probe Context Functions * @@ -1333,6 +1338,35 @@ } /* + * DTrace private version of a function to copy data from an mbuf + * chain starting "off" bytes from the beginning, continuing for "len" + * bytes, into the indicated buffer. + */ +#ifdef __FreeBSD__ +void +dtrace_mbuf_copydata(const struct mbuf *m, int off, int len, uintptr_t cp) +{ + u_int count; + + + while (off > 0) { + if (off < m->m_len) + break; + off -= m->m_len; + m = m->m_next; + } + while (len > 0) { + count = min(m->m_len - off, len); + dtrace_bcopy(mtod(m, caddr_t) + off, (void *)cp, count); + len -= count; + cp += count; + off = 0; + m = m->m_next; + } +} +#endif /* __FreeBSD__ */ + +/* * Compare s1 to s2 using safe memory accesses. The s1 data is assumed to be * unsafe memory specified by the DIF program. The s2 data is assumed to be * safe memory that we can access directly because it is managed by DTrace. @@ -4549,6 +4583,73 @@ break; } +#ifdef __FreeBSD__ + case DIF_SUBR_COPYOUTMBUF: { + uintptr_t dest = mstate->dtms_scratch_ptr; + struct mbuf *m = (struct mbuf *)tupregs[0].dttk_value; + uint64_t len = 0; + size_t scratch_size = 0; + size_t req_size = tupregs[1].dttk_value; + size_t offset = 0; + + if (m == NULL) { + DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); + regs[rd] = 0; + break; + } + + len = m_length(m, NULL); + + /* Optional 3rd argument is an offset into the packet. */ + if (nargs > 2) { + offset = tupregs[2].dttk_value; + /* Be strict with the offset parameter */ + if ((offset < 0) || (offset >= len) || + ((len - offset) > req_size)) { + DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); + regs[rd] = 0; + break; + } + } + + len -= offset; + scratch_size = (dest - mstate->dtms_scratch_ptr) + len; + + /* + * The user can request the whole buffer with arg2 <= + * 0 but if they request a size less than the len of + * the full chain we will respet their request. This + * code also prevents the caller from asking for more + * data than is present in the chain. + */ + + if ((req_size > 0) && (req_size < len)) { + len = req_size; + scratch_size = (dest - mstate->dtms_scratch_ptr) + len; + } + + /* + * Rounding up the user allocation size could have overflowed + * a large, bogus allocation (like -1ULL) to 0. + */ + if (scratch_size < len || + !DTRACE_INSCRATCH(mstate, scratch_size)) { + DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); + regs[rd] = 0; + break; + } + + DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); + dtrace_mbuf_copydata(m, offset, len, dest); + DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); + + mstate->dtms_scratch_ptr += len; + regs[rd] = dest; + break; + } + +#endif /* __FreeBSD__ */ + #ifdef illumos case DIF_SUBR_MSGSIZE: case DIF_SUBR_MSGDSIZE: { @@ -9944,7 +10045,7 @@ err += efunc(pc, "invalid register %u\n", rd); break; case DIF_OP_CALL: - if (subr > DIF_SUBR_MAX) + if (!VALID_SUBR(subr)) err += efunc(pc, "invalid subr %u\n", subr); if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); Index: sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h +++ sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h @@ -315,7 +315,25 @@ #define DIF_SUBR_GETF 51 #define DIF_SUBR_JSON 52 #define DIF_SUBR_STRTOLL 53 -#define DIF_SUBR_MAX 53 /* max subroutine value */ +#define DIF_SUBR_MAX 53 /* max common subroutine value */ + +/* + * DT_ACT (action) routines are placed directly after the base + * subroutines. Leave space up to 200 for these. + */ + +/* Leave space for Apple/xnu specific routines from 200-299 */ + +/* FreeBSD specific subroutines */ + +#define DIF_SUBR_FBSD_MIN 300 +#define DIF_SUBR_COPYOUTMBUF 300 +#define DIF_SUBR_FBSD_MAX 300 + +#define VALID_SUBR(x) \ + (((x >= 0) && (x <= DIF_SUBR_MAX)) || \ + ((x >= DIF_SUBR_FBSD_MIN) && (x <= DIF_SUBR_FBSD_MAX))) + typedef uint32_t dif_instr_t;