Mercurial > illumos > illumos-gate
changeset 2870:a343ed00e23c
6458281 dtrace structure tracing problem
6474442 dtrace would benefit from a few extra safety tests
author | dp |
---|---|
date | Thu, 05 Oct 2006 19:20:42 -0700 |
parents | 324151eecd58 |
children | 9983c0b50e87 |
files | usr/src/cmd/dtrace/test/pkg/SUNWdtrt/prototype_com usr/src/cmd/dtrace/test/tst/common/funcs/tst.strtok_null.d usr/src/cmd/dtrace/test/tst/common/privs/tst.func_access.ksh usr/src/cmd/dtrace/test/tst/common/privs/tst.op_access.ksh usr/src/cmd/dtrace/test/tst/common/privs/tst.unpriv_funcs.ksh usr/src/cmd/dtrace/test/tst/common/safety/tst.execname.d usr/src/cmd/dtrace/test/tst/common/safety/tst.pid.d usr/src/cmd/dtrace/test/tst/common/safety/tst.zonename.d usr/src/uts/common/dtrace/dtrace.c usr/src/uts/common/sys/dtrace_impl.h |
diffstat | 10 files changed, 820 insertions(+), 41 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/cmd/dtrace/test/pkg/SUNWdtrt/prototype_com Thu Oct 05 17:39:16 2006 -0700 +++ b/usr/src/cmd/dtrace/test/pkg/SUNWdtrt/prototype_com Thu Oct 05 19:20:42 2006 -0700 @@ -537,6 +537,7 @@ f none opt/SUNWdtrt/tst/common/funcs/tst.strstr.d.out 0444 root bin f none opt/SUNWdtrt/tst/common/funcs/tst.strtok.d 0444 root bin f none opt/SUNWdtrt/tst/common/funcs/tst.strtok.d.out 0444 root bin +f none opt/SUNWdtrt/tst/common/funcs/tst.strtok_null.d 0444 root bin f none opt/SUNWdtrt/tst/common/funcs/tst.substr.d 0444 root bin f none opt/SUNWdtrt/tst/common/funcs/tst.substr.d.out 0444 root bin f none opt/SUNWdtrt/tst/common/funcs/tst.system.d 0444 root bin @@ -657,6 +658,8 @@ f none opt/SUNWdtrt/tst/common/pid/tst.emptystack.exe 0555 root bin f none opt/SUNWdtrt/tst/common/pid/tst.float.d 0444 root bin f none opt/SUNWdtrt/tst/common/pid/tst.float.exe 0555 root bin +f none opt/SUNWdtrt/tst/common/pid/tst.fork.d 0444 root bin +f none opt/SUNWdtrt/tst/common/pid/tst.fork.exe 0555 root bin f none opt/SUNWdtrt/tst/common/pid/tst.gcc.d 0444 root bin f none opt/SUNWdtrt/tst/common/pid/tst.gcc.exe 0555 root bin f none opt/SUNWdtrt/tst/common/pid/tst.manypids.ksh 0444 root bin @@ -836,6 +839,10 @@ f none opt/SUNWdtrt/tst/common/printf/tst.widths1.d 0444 root bin f none opt/SUNWdtrt/tst/common/printf/tst.wp.d 0444 root bin f none opt/SUNWdtrt/tst/common/printf/tst.wp.d.out 0444 root bin +d none opt/SUNWdtrt/tst/common/privs 0755 root bin +f none opt/SUNWdtrt/tst/common/privs/tst.func_access.ksh 0444 root bin +f none opt/SUNWdtrt/tst/common/privs/tst.op_access.ksh 0444 root bin +f none opt/SUNWdtrt/tst/common/privs/tst.unpriv_funcs.ksh 0444 root bin d none opt/SUNWdtrt/tst/common/probes 0755 root bin f none opt/SUNWdtrt/tst/common/probes/err.D_PDESC_ZERO.probeqtn.d 0444 root bin f none opt/SUNWdtrt/tst/common/probes/err.D_PDESC_ZERO.probestar.d 0444 root bin @@ -958,6 +965,7 @@ f none opt/SUNWdtrt/tst/common/safety/tst.copyin.d 0444 root bin f none opt/SUNWdtrt/tst/common/safety/tst.ddi_pathname.d 0444 root bin f none opt/SUNWdtrt/tst/common/safety/tst.dirname.d 0444 root bin +f none opt/SUNWdtrt/tst/common/safety/tst.execname.d 0444 root bin f none opt/SUNWdtrt/tst/common/safety/tst.hton.d 0444 root bin f none opt/SUNWdtrt/tst/common/safety/tst.errno.d 0444 root bin f none opt/SUNWdtrt/tst/common/safety/tst.gid.d 0444 root bin @@ -965,6 +973,7 @@ f none opt/SUNWdtrt/tst/common/safety/tst.msgdsize.d 0444 root bin f none opt/SUNWdtrt/tst/common/safety/tst.msgsize.d 0444 root bin f none opt/SUNWdtrt/tst/common/safety/tst.null.d 0444 root bin +f none opt/SUNWdtrt/tst/common/safety/tst.pid.d 0444 root bin f none opt/SUNWdtrt/tst/common/safety/tst.ppid.d 0444 root bin f none opt/SUNWdtrt/tst/common/safety/tst.progenyof.d 0444 root bin f none opt/SUNWdtrt/tst/common/safety/tst.random.d 0444 root bin @@ -984,6 +993,7 @@ f none opt/SUNWdtrt/tst/common/safety/tst.ustackdepth.d 0444 root bin f none opt/SUNWdtrt/tst/common/safety/tst.vahole.d 0444 root bin f none opt/SUNWdtrt/tst/common/safety/tst.violentdeath.ksh 0444 root bin +f none opt/SUNWdtrt/tst/common/safety/tst.zonename.d 0444 root bin d none opt/SUNWdtrt/tst/common/scalars 0755 root bin f none opt/SUNWdtrt/tst/common/scalars/err.D_ARR_LOCAL.thisarray.d 0444 root bin f none opt/SUNWdtrt/tst/common/scalars/err.D_DECL_CLASS.selfthis.d 0444 root bin
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/dtrace/test/tst/common/funcs/tst.strtok_null.d Thu Oct 05 19:20:42 2006 -0700 @@ -0,0 +1,43 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Test that a strtok(NULL, ...) without first calling strtok(string, ...) + * produces an error + */ + +BEGIN +{ + trace(strtok(NULL, "!")); + exit(1); +} + +ERROR +{ + exit(0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/dtrace/test/tst/common/privs/tst.func_access.ksh Thu Oct 05 19:20:42 2006 -0700 @@ -0,0 +1,82 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +ppriv -s A=basic,dtrace_proc,dtrace_user $$ + +/usr/sbin/dtrace -q -s /dev/stdin <<"EOF" + +BEGIN { + errorcount = 0; + expected_errorcount = 23; +} + +BEGIN { trace(mutex_owned(&`pidlock)); } +BEGIN { trace(mutex_owner(&`pidlock)); } +BEGIN { trace(mutex_type_adaptive(&`pidlock)); } +BEGIN { trace(mutex_type_spin(&`pidlock)); } + +BEGIN { trace(rw_read_held(&`ksyms_lock)); } +BEGIN { trace(rw_write_held(&`ksyms_lock)); } +BEGIN { trace(rw_iswriter(&`ksyms_lock)); } + +BEGIN { x = alloca(10); bcopy(`initname, x, 10); trace(stringof(x)); } +/* We have no reliable way to test msgsize */ + +BEGIN { trace(strlen(`initname)); } +BEGIN { trace(strchr(`initname, 0x69)); } +BEGIN { trace(strrchr(`initname, 0x69)); } +BEGIN { trace(strstr("/sbin/init/foo", `initname)); } +BEGIN { trace(strstr(`initname, "in")); } +BEGIN { trace(strtok(`initname, "/")); } +BEGIN { trace(strtok(NULL, "/")); } +BEGIN { trace(strtok("foo/bar", `initname)); } +BEGIN { trace(strtok(NULL, `initname)); } +BEGIN { trace(substr(`initname, 2, 3)); } + +BEGIN { trace(ddi_pathname(`top_devinfo, 1)); } +BEGIN { trace(strjoin(`initname, "foo")); } +BEGIN { trace(strjoin("foo", `initname)); } +BEGIN { trace(dirname(`initname)); } +BEGIN { trace(cleanpath(`initname)); } + +ERROR { + errorcount++; +} + +BEGIN /errorcount == expected_errorcount/ { + trace("test passed"); + exit(0); +} + +BEGIN /errorcount != expected_errorcount/ { + printf("fail: expected %d. saw %d.", expected_errorcount, errorcount); + exit(1); +} +EOF + + +exit $?
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/dtrace/test/tst/common/privs/tst.op_access.ksh Thu Oct 05 19:20:42 2006 -0700 @@ -0,0 +1,70 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +ppriv -s A=basic,dtrace_proc,dtrace_user $$ + +/usr/sbin/dtrace -q -s /dev/stdin <<"EOF" +BEGIN { + errorcount = 0; + expected_errorcount = 7; +} + +/* BYREF */ +BEGIN { trace(`utsname); } +BEGIN { trace(`kmem_flags); } + +/* DIF_OP_SCMP */ +BEGIN /`initname == "/sbin/init"/ { trace("bad"); } + +/* DIF_OP_COPYS */ +BEGIN { p = `p0; trace(p); } + +/* DIF_OP_STTS */ +BEGIN { self->p = `p0; trace(self->p); } + +/* DIF_OP_STGAA */ +BEGIN { a[stringof(`initname)] = 42; trace(a["/sbin/init"]); } + +/* DIF_OP_STTAA */ +BEGIN { self->a[stringof(`initname)] = 42; trace(self->a["/sbin/init"]); } + +ERROR { + errorcount++; +} + +BEGIN /errorcount == expected_errorcount/ { + trace("pass"); + exit(0); +} + +BEGIN /errorcount != expected_errorcount/ { + printf("fail: expected %d. saw %d.", expected_errorcount, errorcount); + exit(1); +} +EOF + +exit $?
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/dtrace/test/tst/common/privs/tst.unpriv_funcs.ksh Thu Oct 05 19:20:42 2006 -0700 @@ -0,0 +1,79 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +# +# Affirmative test of less privileged user operation. We do so by running +# this test case as root, then as a less privileged user. The output should +# be exactly the same. +# + +script() +{ + cat <<"EOF" + + BEGIN { trace("trace\n"); } + BEGIN { printf("%s\n", "printf"); } + BEGIN { printf("strlen(\"strlen\") = %d\n", strlen("strlen")); } + BEGIN { x = alloca(10); + bcopy("alloca\n", x, 7); + trace(stringof(x)); } + + BEGIN { printf("index(\"index\", \"x\") = %d\n", + index("index", "x")); } + BEGIN { printf("strchr(\"strchr\", \'t\') = %s\n", + strchr("strchr", 't')); } + + BEGIN { printf("strtok(\"strtok\", \"t\") = %s\n", + strtok("strtok", "t")); } + BEGIN { printf("strtok(NULL, \"t\") = %s\n", + strtok(NULL, "t")); } + BEGIN { printf("strtok(NULL, \"t\") = %s\n", + strtok(NULL, "t")); } + BEGIN { printf("substr(\"substr\", 2, 2) = %s\n", + substr("substr", 2, 2)); } + BEGIN { trace(strjoin("str", "join\n")); } + BEGIN { trace(basename("dirname/basename")); trace("/"); } + BEGIN { trace(dirname("dirname/basename")); } + + BEGIN { exit(0); } + ERROR { exit(1); } +EOF +} + +privout=/tmp/$$.priv_output +unprivout=/tmp/$$.unpriv_output + +script | /usr/sbin/dtrace -q -s /dev/stdin > $privout +ppriv -s A=basic,dtrace_user $$ +script | /usr/sbin/dtrace -q -s /dev/stdin > $unprivout + +diff $privout $unprivout +res=$? + +/bin/rm -f $privout $unprivout + +exit $res
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/dtrace/test/tst/common/safety/tst.execname.d Thu Oct 05 19:20:42 2006 -0700 @@ -0,0 +1,49 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * ASSERTION: + * collect execname at every fbt probe and at every firing of a + * high-frequency profile probe + */ + +fbt::: +{ + @a[execname] = count(); +} + +profile-4999hz +{ + @a[execname] = count(); +} + +tick-1sec +/n++ == 10/ +{ + exit(0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/dtrace/test/tst/common/safety/tst.pid.d Thu Oct 05 19:20:42 2006 -0700 @@ -0,0 +1,49 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * ASSERTION: + * collect pid at every fbt probe and at every firing of a + * high-frequency profile probe + */ + +fbt::: +{ + @a[pid] = count(); +} + +profile-4999hz +{ + @a[pid] = count(); +} + +tick-1sec +/n++ == 10/ +{ + exit(0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/dtrace/test/tst/common/safety/tst.zonename.d Thu Oct 05 19:20:42 2006 -0700 @@ -0,0 +1,49 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * ASSERTION: + * collect zonename at every fbt probe and at every firing of a + * high-frequency profile probe + */ + +fbt::: +{ + @a[zonename] = count(); +} + +profile-4999hz +{ + @a[zonename] = count(); +} + +tick-1sec +/n++ == 10/ +{ + exit(0); +}
--- a/usr/src/uts/common/dtrace/dtrace.c Thu Oct 05 17:39:16 2006 -0700 +++ b/usr/src/uts/common/dtrace/dtrace.c Thu Oct 05 19:20:42 2006 -0700 @@ -352,6 +352,15 @@ #define DTRACE_ALIGNCHECK(addr, size, flags) #endif +/* + * Test whether a range of memory starting at testaddr of size testsz falls + * within the range of memory described by addr, sz, taking care to avoid + * problems with overflow and underflow of the unsigned quantities. + */ +#define DTRACE_INRANGE(testaddr, testsz, baseaddr, basesz) \ + ((testaddr) - (baseaddr) < (basesz) && \ + (testaddr) + (testsz) - (baseaddr) <= (basesz)) + #define DTRACE_LOADFUNC(bits) \ /*CSTYLED*/ \ uint##bits##_t \ @@ -419,6 +428,7 @@ ((act)->dta_kind == DTRACEACT_DIFEXPR && \ (act)->dta_difo->dtdo_rtype.dtdt_kind == DIF_TYPE_STRING) +static size_t dtrace_strlen(const char *, size_t); static dtrace_probe_t *dtrace_probe_lookup_id(dtrace_id_t id); static void dtrace_enabling_provide(dtrace_provider_t *); static int dtrace_enabling_match(dtrace_enabling_t *, int *); @@ -549,8 +559,7 @@ if (svar == NULL || svar->dtsv_size == 0) continue; - if (addr - svar->dtsv_data < svar->dtsv_size && - addr + sz <= svar->dtsv_data + svar->dtsv_size) + if (DTRACE_INRANGE(addr, sz, svar->dtsv_data, svar->dtsv_size)) return (1); } @@ -567,16 +576,11 @@ dtrace_canstore(uint64_t addr, size_t sz, dtrace_mstate_t *mstate, dtrace_vstate_t *vstate) { - uintptr_t a; - size_t s; - /* * First, check to see if the address is in scratch space... */ - a = mstate->dtms_scratch_base; - s = mstate->dtms_scratch_size; - - if (addr - a < s && addr + sz <= a + s) + if (DTRACE_INRANGE(addr, sz, mstate->dtms_scratch_base, + mstate->dtms_scratch_size)) return (1); /* @@ -584,9 +588,8 @@ * up both thread-local variables and any global dynamically-allocated * variables. */ - a = (uintptr_t)vstate->dtvs_dynvars.dtds_base; - s = vstate->dtvs_dynvars.dtds_size; - if (addr - a < s && addr + sz <= a + s) + if (DTRACE_INRANGE(addr, sz, (uintptr_t)vstate->dtvs_dynvars.dtds_base, + vstate->dtvs_dynvars.dtds_size)) return (1); /* @@ -604,6 +607,100 @@ return (0); } + +/* + * Convenience routine to check to see if the address is within a memory + * region in which a load may be issued given the user's privilege level; + * if not, it sets the appropriate error flags and loads 'addr' into the + * illegal value slot. + * + * DTrace subroutines (DIF_SUBR_*) should use this helper to implement + * appropriate memory access protection. + */ +static int +dtrace_canload(uint64_t addr, size_t sz, dtrace_mstate_t *mstate, + dtrace_vstate_t *vstate) +{ + volatile uintptr_t *illval = &cpu_core[CPU->cpu_id].cpuc_dtrace_illval; + + /* + * If we hold the privilege to read from kernel memory, then + * everything is readable. + */ + if ((mstate->dtms_access & DTRACE_ACCESS_KERNEL) != 0) + return (1); + + /* + * You can obviously read that which you can store. + */ + if (dtrace_canstore(addr, sz, mstate, vstate)) + return (1); + + /* + * We're allowed to read from our own string table. + */ + if (DTRACE_INRANGE(addr, sz, (uintptr_t)mstate->dtms_difo->dtdo_strtab, + mstate->dtms_difo->dtdo_strlen)) + return (1); + + DTRACE_CPUFLAG_SET(CPU_DTRACE_KPRIV); + *illval = addr; + return (0); +} + +/* + * Convenience routine to check to see if a given string is within a memory + * region in which a load may be issued given the user's privilege level; + * this exists so that we don't need to issue unnecessary dtrace_strlen() + * calls in the event that the user has all privileges. + */ +static int +dtrace_strcanload(uint64_t addr, size_t sz, dtrace_mstate_t *mstate, + dtrace_vstate_t *vstate) +{ + size_t strsz; + + /* + * If we hold the privilege to read from kernel memory, then + * everything is readable. + */ + if ((mstate->dtms_access & DTRACE_ACCESS_KERNEL) != 0) + return (1); + + strsz = 1 + dtrace_strlen((char *)(uintptr_t)addr, sz); + if (dtrace_canload(addr, strsz, mstate, vstate)) + return (1); + + return (0); +} + +/* + * Convenience routine to check to see if a given variable is within a memory + * region in which a load may be issued given the user's privilege level. + */ +static int +dtrace_vcanload(void *src, dtrace_diftype_t *type, dtrace_mstate_t *mstate, + dtrace_vstate_t *vstate) +{ + size_t sz; + ASSERT(type->dtdt_flags & DIF_TF_BYREF); + + /* + * If we hold the privilege to read from kernel memory, then + * everything is readable. + */ + if ((mstate->dtms_access & DTRACE_ACCESS_KERNEL) != 0) + return (1); + + if (type->dtdt_kind == DIF_TYPE_STRING) + sz = dtrace_strlen(src, + vstate->dtvs_state->dts_options[DTRACEOPT_STRSIZE]) + 1; + else + sz = type->dtdt_size; + + return (dtrace_canload((uintptr_t)src, sz, mstate, vstate)); +} + /* * Compare two strings using safe loads. */ @@ -1034,7 +1131,8 @@ */ dtrace_dynvar_t * dtrace_dynvar(dtrace_dstate_t *dstate, uint_t nkeys, - dtrace_key_t *key, size_t dsize, dtrace_dynvar_op_t op) + dtrace_key_t *key, size_t dsize, dtrace_dynvar_op_t op, + dtrace_mstate_t *mstate, dtrace_vstate_t *vstate) { uint64_t hashval = DTRACE_DYNHASH_VALID; dtrace_dynhash_t *hash = dstate->dtds_hash; @@ -1086,6 +1184,9 @@ uint64_t j, size = key[i].dttk_size; uintptr_t base = (uintptr_t)key[i].dttk_value; + if (!dtrace_canload(base, size, mstate, vstate)) + break; + for (j = 0; j < size; j++) { hashval += dtrace_load8(base + j); hashval += (hashval << 10); @@ -1094,6 +1195,9 @@ } } + if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_FAULT)) + return (NULL); + hashval += (hashval << 3); hashval ^= (hashval >> 11); hashval += (hashval << 15); @@ -1480,7 +1584,7 @@ dvar->dtdv_next = free; } while (dtrace_casptr(&dcpu->dtdsc_dirty, free, dvar) != free); - return (dtrace_dynvar(dstate, nkeys, key, dsize, op)); + return (dtrace_dynvar(dstate, nkeys, key, dsize, op, mstate, vstate)); } /*ARGSUSED*/ @@ -2254,6 +2358,50 @@ } /* + * Return a string. In the event that the user lacks the privilege to access + * arbitrary kernel memory, we copy the string out to scratch memory so that we + * don't fail access checking. + * + * dtrace_dif_variable() uses this routine as a helper for various + * builtin values such as 'execname' and 'probefunc.' + */ +uintptr_t +dtrace_dif_varstr(uintptr_t addr, dtrace_state_t *state, + dtrace_mstate_t *mstate) +{ + uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; + uintptr_t ret; + size_t strsz; + + /* + * The easy case: this probe is allowed to read all of memory, so + * we can just return this as a vanilla pointer. + */ + if ((mstate->dtms_access & DTRACE_ACCESS_KERNEL) != 0) + return (addr); + + /* + * This is the tougher case: we copy the string in question from + * kernel memory into scratch memory and return it that way: this + * ensures that we won't trip up when access checking tests the + * BYREF return value. + */ + strsz = dtrace_strlen((char *)addr, size) + 1; + + if (mstate->dtms_scratch_ptr + strsz > + mstate->dtms_scratch_base + mstate->dtms_scratch_size) { + DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); + return (NULL); + } + + dtrace_strcpy((const void *)addr, (void *)mstate->dtms_scratch_ptr, + strsz); + ret = mstate->dtms_scratch_ptr; + mstate->dtms_scratch_ptr += strsz; + return (ret); +} + +/* * This function implements the DIF emulator's variable lookups. The emulator * passes a reserved variable identifier and optional built-in array index. */ @@ -2449,23 +2597,27 @@ case DIF_VAR_PROBEPROV: ASSERT(mstate->dtms_present & DTRACE_MSTATE_PROBE); - return ((uint64_t)(uintptr_t) - mstate->dtms_probe->dtpr_provider->dtpv_name); + return (dtrace_dif_varstr( + (uintptr_t)mstate->dtms_probe->dtpr_provider->dtpv_name, + state, mstate)); case DIF_VAR_PROBEMOD: ASSERT(mstate->dtms_present & DTRACE_MSTATE_PROBE); - return ((uint64_t)(uintptr_t) - mstate->dtms_probe->dtpr_mod); + return (dtrace_dif_varstr( + (uintptr_t)mstate->dtms_probe->dtpr_mod, + state, mstate)); case DIF_VAR_PROBEFUNC: ASSERT(mstate->dtms_present & DTRACE_MSTATE_PROBE); - return ((uint64_t)(uintptr_t) - mstate->dtms_probe->dtpr_func); + return (dtrace_dif_varstr( + (uintptr_t)mstate->dtms_probe->dtpr_func, + state, mstate)); case DIF_VAR_PROBENAME: ASSERT(mstate->dtms_present & DTRACE_MSTATE_PROBE); - return ((uint64_t)(uintptr_t) - mstate->dtms_probe->dtpr_name); + return (dtrace_dif_varstr( + (uintptr_t)mstate->dtms_probe->dtpr_name, + state, mstate)); case DIF_VAR_PID: if (!dtrace_priv_proc(state)) @@ -2532,8 +2684,9 @@ * (This is true because threads don't clean up their own * state -- they leave that task to whomever reaps them.) */ - return ((uint64_t)(uintptr_t) - curthread->t_procp->p_user.u_comm); + return (dtrace_dif_varstr( + (uintptr_t)curthread->t_procp->p_user.u_comm, + state, mstate)); case DIF_VAR_ZONENAME: if (!dtrace_priv_proc(state)) @@ -2551,8 +2704,9 @@ * (This is true because threads don't clean up their own * state -- they leave that task to whomever reaps them.) */ - return ((uint64_t)(uintptr_t) - curthread->t_procp->p_zone->zone_name); + return (dtrace_dif_varstr( + (uintptr_t)curthread->t_procp->p_zone->zone_name, + state, mstate)); case DIF_VAR_UID: if (!dtrace_priv_proc(state)) @@ -2638,6 +2792,7 @@ { volatile uint16_t *flags = &cpu_core[CPU->cpu_id].cpuc_dtrace_flags; volatile uintptr_t *illval = &cpu_core[CPU->cpu_id].cpuc_dtrace_illval; + dtrace_vstate_t *vstate = &state->dts_vstate; union { mutex_impl_t mi; @@ -2655,6 +2810,12 @@ break; case DIF_SUBR_MUTEX_OWNED: + if (!dtrace_canload(tupregs[0].dttk_value, sizeof (kmutex_t), + mstate, vstate)) { + regs[rd] = NULL; + break; + } + m.mx = dtrace_load64(tupregs[0].dttk_value); if (MUTEX_TYPE_ADAPTIVE(&m.mi)) regs[rd] = MUTEX_OWNER(&m.mi) != MUTEX_NO_OWNER; @@ -2663,6 +2824,12 @@ break; case DIF_SUBR_MUTEX_OWNER: + if (!dtrace_canload(tupregs[0].dttk_value, sizeof (kmutex_t), + mstate, vstate)) { + regs[rd] = NULL; + break; + } + m.mx = dtrace_load64(tupregs[0].dttk_value); if (MUTEX_TYPE_ADAPTIVE(&m.mi) && MUTEX_OWNER(&m.mi) != MUTEX_NO_OWNER) @@ -2672,11 +2839,23 @@ break; case DIF_SUBR_MUTEX_TYPE_ADAPTIVE: + if (!dtrace_canload(tupregs[0].dttk_value, sizeof (kmutex_t), + mstate, vstate)) { + regs[rd] = NULL; + break; + } + m.mx = dtrace_load64(tupregs[0].dttk_value); regs[rd] = MUTEX_TYPE_ADAPTIVE(&m.mi); break; case DIF_SUBR_MUTEX_TYPE_SPIN: + if (!dtrace_canload(tupregs[0].dttk_value, sizeof (kmutex_t), + mstate, vstate)) { + regs[rd] = NULL; + break; + } + m.mx = dtrace_load64(tupregs[0].dttk_value); regs[rd] = MUTEX_TYPE_SPIN(&m.mi); break; @@ -2684,17 +2863,35 @@ case DIF_SUBR_RW_READ_HELD: { uintptr_t tmp; + if (!dtrace_canload(tupregs[0].dttk_value, sizeof (uintptr_t), + mstate, vstate)) { + regs[rd] = NULL; + break; + } + r.rw = dtrace_loadptr(tupregs[0].dttk_value); regs[rd] = _RW_READ_HELD(&r.ri, tmp); break; } case DIF_SUBR_RW_WRITE_HELD: + if (!dtrace_canload(tupregs[0].dttk_value, sizeof (krwlock_t), + mstate, vstate)) { + regs[rd] = NULL; + break; + } + r.rw = dtrace_loadptr(tupregs[0].dttk_value); regs[rd] = _RW_WRITE_HELD(&r.ri); break; case DIF_SUBR_RW_ISWRITER: + if (!dtrace_canload(tupregs[0].dttk_value, sizeof (krwlock_t), + mstate, vstate)) { + regs[rd] = NULL; + break; + } + r.rw = dtrace_loadptr(tupregs[0].dttk_value); regs[rd] = _RW_ISWRITER(&r.ri); break; @@ -2714,6 +2911,11 @@ break; } + if (!dtrace_canload(src, size, mstate, vstate)) { + regs[rd] = NULL; + break; + } + dtrace_bcopy((void *)src, (void *)dest, size); break; } @@ -2806,6 +3008,13 @@ int cont = 0; while (baddr != NULL && !(*flags & CPU_DTRACE_FAULT)) { + + if (!dtrace_canload(baddr, sizeof (mblk_t), mstate, + vstate)) { + regs[rd] = NULL; + break; + } + wptr = dtrace_loadptr(baddr + offsetof(mblk_t, b_wptr)); @@ -2903,11 +3112,21 @@ break; } - case DIF_SUBR_STRLEN: - regs[rd] = dtrace_strlen((char *)(uintptr_t) - tupregs[0].dttk_value, + case DIF_SUBR_STRLEN: { + size_t sz; + uintptr_t addr = (uintptr_t)tupregs[0].dttk_value; + sz = dtrace_strlen((char *)addr, state->dts_options[DTRACEOPT_STRSIZE]); + + if (!dtrace_canload(addr, sz + 1, mstate, vstate)) { + regs[rd] = NULL; + break; + } + + regs[rd] = sz; + break; + } case DIF_SUBR_STRCHR: case DIF_SUBR_STRRCHR: { @@ -2918,6 +3137,7 @@ * is DIF_SUBR_STRRCHR, we will look for the last occurrence * of the specified character instead of the first. */ + uintptr_t saddr = tupregs[0].dttk_value; uintptr_t addr = tupregs[0].dttk_value; uintptr_t limit = addr + state->dts_options[DTRACEOPT_STRSIZE]; char c, target = (char)tupregs[1].dttk_value; @@ -2934,6 +3154,11 @@ break; } + if (!dtrace_canload(saddr, addr - saddr, mstate, vstate)) { + regs[rd] = NULL; + break; + } + break; } @@ -2960,6 +3185,17 @@ regs[rd] = notfound; + if (!dtrace_canload((uintptr_t)addr, len + 1, mstate, vstate)) { + regs[rd] = NULL; + break; + } + + if (!dtrace_canload((uintptr_t)substr, sublen + 1, mstate, + vstate)) { + regs[rd] = NULL; + break; + } + /* * strstr() and index()/rindex() have similar semantics if * both strings are the empty string: strstr() returns a @@ -3085,6 +3321,15 @@ char *dest = (char *)mstate->dtms_scratch_ptr; int i; + /* + * Check both the token buffer and (later) the input buffer, + * since both could be non-scratch addresses. + */ + if (!dtrace_strcanload(tokaddr, size, mstate, vstate)) { + regs[rd] = NULL; + break; + } + if (mstate->dtms_scratch_ptr + size > mstate->dtms_scratch_base + mstate->dtms_scratch_size) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); @@ -3101,6 +3346,19 @@ * it behaves like an implicit clause-local variable. */ addr = mstate->dtms_strtok; + } else { + /* + * If the user-specified address is non-NULL we must + * access check it. This is the only time we have + * a chance to do so, since this address may reside + * in the string table of this clause-- future calls + * (when we fetch addr from mstate->dtms_strtok) + * would fail this access check. + */ + if (!dtrace_strcanload(addr, size, mstate, vstate)) { + regs[rd] = NULL; + break; + } } /* @@ -3174,6 +3432,11 @@ size_t len = dtrace_strlen((char *)s, size); int64_t i = 0; + if (!dtrace_canload(s, len + 1, mstate, vstate)) { + regs[rd] = NULL; + break; + } + if (nargs <= 2) remaining = (int64_t)size; @@ -3242,6 +3505,17 @@ char *s; int i, len, depth = 0; + /* + * Due to all the pointer jumping we do and context we must + * rely upon, we just mandate that the user must have kernel + * read privileges to use this routine. + */ + if ((mstate->dtms_access & DTRACE_ACCESS_KERNEL) == 0) { + *flags |= CPU_DTRACE_KPRIV; + *illval = daddr; + regs[rd] = NULL; + } + if (size == 0 || mstate->dtms_scratch_ptr + size > mstate->dtms_scratch_base + mstate->dtms_scratch_size) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); @@ -3417,6 +3691,12 @@ uintptr_t s2 = tupregs[1].dttk_value; int i = 0; + if (!dtrace_strcanload(s1, size, mstate, vstate) || + !dtrace_strcanload(s2, size, mstate, vstate)) { + regs[rd] = NULL; + break; + } + if (mstate->dtms_scratch_ptr + size > mstate->dtms_scratch_base + mstate->dtms_scratch_size) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); @@ -3522,6 +3802,11 @@ int lastbase = -1, firstbase = -1, lastdir = -1; int start, end; + if (!dtrace_canload(src, len + 1, mstate, vstate)) { + regs[rd] = NULL; + break; + } + if (mstate->dtms_scratch_ptr + size > mstate->dtms_scratch_base + mstate->dtms_scratch_size) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); @@ -3646,6 +3931,11 @@ uintptr_t src = tupregs[0].dttk_value; int i = 0, j = 0; + if (!dtrace_strcanload(src, size, mstate, vstate)) { + regs[rd] = NULL; + break; + } + if (mstate->dtms_scratch_ptr + size > mstate->dtms_scratch_base + mstate->dtms_scratch_size) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); @@ -3778,6 +4068,12 @@ dif_instr_t instr; uint_t r1, r2, rd; + /* + * We stash the current DIF object into the machine state: we need it + * for subsequent access checking. + */ + mstate->dtms_difo = difo; + regs[DIF_REG_R0] = 0; /* %r0 is fixed at zero */ while (pc < textlen && !(*flags & CPU_DTRACE_FAULT)) { @@ -4021,15 +4317,25 @@ regs[rd] = (uint64_t)(uintptr_t) (strtab + DIF_INSTR_STRING(instr)); break; - case DIF_OP_SCMP: - cc_r = dtrace_strncmp((char *)(uintptr_t)regs[r1], - (char *)(uintptr_t)regs[r2], - state->dts_options[DTRACEOPT_STRSIZE]); + case DIF_OP_SCMP: { + size_t sz = state->dts_options[DTRACEOPT_STRSIZE]; + uintptr_t s1 = regs[r1]; + uintptr_t s2 = regs[r2]; + + if (s1 != NULL && + !dtrace_strcanload(s1, sz, mstate, vstate)) + break; + if (s2 != NULL && + !dtrace_strcanload(s2, sz, mstate, vstate)) + break; + + cc_r = dtrace_strncmp((char *)s1, (char *)s2, sz); cc_n = cc_r < 0; cc_z = cc_r == 0; cc_v = cc_c = 0; break; + } case DIF_OP_LDGA: regs[rd] = dtrace_dif_variable(mstate, state, r1, regs[r2]); @@ -4092,6 +4398,10 @@ *(uint8_t *)a = 0; a += sizeof (uint64_t); } + if (!dtrace_vcanload( + (void *)(uintptr_t)regs[rd], &v->dtdv_type, + mstate, vstate)) + break; dtrace_vcopy((void *)(uintptr_t)regs[rd], (void *)a, &v->dtdv_type); @@ -4185,6 +4495,11 @@ a += sizeof (uint64_t); } + if (!dtrace_vcanload( + (void *)(uintptr_t)regs[rd], &v->dtdv_type, + mstate, vstate)) + break; + dtrace_vcopy((void *)(uintptr_t)regs[rd], (void *)a, &v->dtdv_type); break; @@ -4211,7 +4526,8 @@ key[1].dttk_size = 0; dvar = dtrace_dynvar(dstate, 2, key, - sizeof (uint64_t), DTRACE_DYNVAR_NOALLOC); + sizeof (uint64_t), DTRACE_DYNVAR_NOALLOC, + mstate, vstate); if (dvar == NULL) { regs[rd] = 0; @@ -4246,7 +4562,7 @@ v->dtdv_type.dtdt_size > sizeof (uint64_t) ? v->dtdv_type.dtdt_size : sizeof (uint64_t), regs[rd] ? DTRACE_DYNVAR_ALLOC : - DTRACE_DYNVAR_DEALLOC); + DTRACE_DYNVAR_DEALLOC, mstate, vstate); /* * Given that we're storing to thread-local data, @@ -4258,6 +4574,11 @@ break; if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) { + if (!dtrace_vcanload( + (void *)(uintptr_t)regs[rd], + &v->dtdv_type, mstate, vstate)) + break; + dtrace_vcopy((void *)(uintptr_t)regs[rd], dvar->dtdv_data, &v->dtdv_type); } else { @@ -4345,7 +4666,7 @@ dvar = dtrace_dynvar(dstate, nkeys, key, v->dtdv_type.dtdt_size > sizeof (uint64_t) ? v->dtdv_type.dtdt_size : sizeof (uint64_t), - DTRACE_DYNVAR_NOALLOC); + DTRACE_DYNVAR_NOALLOC, mstate, vstate); if (dvar == NULL) { regs[rd] = 0; @@ -4386,12 +4707,17 @@ v->dtdv_type.dtdt_size > sizeof (uint64_t) ? v->dtdv_type.dtdt_size : sizeof (uint64_t), regs[rd] ? DTRACE_DYNVAR_ALLOC : - DTRACE_DYNVAR_DEALLOC); + DTRACE_DYNVAR_DEALLOC, mstate, vstate); if (dvar == NULL) break; if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) { + if (!dtrace_vcanload( + (void *)(uintptr_t)regs[rd], &v->dtdv_type, + mstate, vstate)) + break; + dtrace_vcopy((void *)(uintptr_t)regs[rd], dvar->dtdv_data, &v->dtdv_type); } else { @@ -4427,6 +4753,9 @@ break; } + if (!dtrace_canload(regs[r1], regs[r2], mstate, vstate)) + break; + dtrace_bcopy((void *)(uintptr_t)regs[r1], (void *)(uintptr_t)regs[rd], (size_t)regs[r2]); break; @@ -4835,7 +5164,9 @@ if (vtime && curthread->t_dtrace_start) curthread->t_dtrace_vtime += now - curthread->t_dtrace_start; + mstate.dtms_difo = NULL; mstate.dtms_probe = probe; + mstate.dtms_strtok = NULL; mstate.dtms_arg[0] = arg0; mstate.dtms_arg[1] = arg1; mstate.dtms_arg[2] = arg2; @@ -5005,6 +5336,11 @@ mstate.dtms_epid = ecb->dte_epid; mstate.dtms_present |= DTRACE_MSTATE_EPID; + if (state->dts_cred.dcr_visible & DTRACE_CRV_KERNEL) + mstate.dtms_access = DTRACE_ACCESS_KERNEL; + else + mstate.dtms_access = 0; + if (pred != NULL) { dtrace_difo_t *dp = pred->dtp_difo; int rval; @@ -5268,6 +5604,10 @@ if (dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF) { uintptr_t end = valoffs + size; + if (!dtrace_vcanload((void *)(uintptr_t)val, + &dp->dtdo_rtype, &mstate, vstate)) + continue; + /* * If this is a string, we're going to only * load until we find the zero byte -- after @@ -7111,11 +7451,11 @@ { int err = 0, i; int (*efunc)(uint_t pc, const char *, ...) = dtrace_difo_err; - int kcheck; + int kcheckload; uint_t pc; - kcheck = cr == NULL || - PRIV_POLICY_ONLY(cr, PRIV_DTRACE_KERNEL, B_FALSE) == 0; + kcheckload = cr == NULL || + (vstate->dtvs_state->dts_cred.dcr_visible & DTRACE_CRV_KERNEL) == 0; dp->dtdo_destructive = 0; @@ -7183,7 +7523,7 @@ err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to %r0\n"); - if (kcheck) + if (kcheckload) dp->dtdo_buf[pc] = DIF_INSTR_LOAD(op + DIF_OP_RLDSB - DIF_OP_LDSB, r1, rd); break;
--- a/usr/src/uts/common/sys/dtrace_impl.h Thu Oct 05 17:39:16 2006 -0700 +++ b/usr/src/uts/common/sys/dtrace_impl.h Thu Oct 05 19:20:42 2006 -0700 @@ -910,6 +910,8 @@ int dtms_ipl; /* cached interrupt pri lev */ int dtms_fltoffs; /* faulting DIFO offset */ uintptr_t dtms_strtok; /* saved strtok() pointer */ + uint32_t dtms_access; /* memory access rights */ + dtrace_difo_t *dtms_difo; /* current dif object */ } dtrace_mstate_t; #define DTRACE_COND_OWNER 0x1 @@ -919,6 +921,12 @@ #define DTRACE_PROBEKEY_MAXDEPTH 8 /* max glob recursion depth */ /* + * Access flag used by dtrace_mstate.dtms_access. + */ +#define DTRACE_ACCESS_KERNEL 0x1 /* the priv to read kmem */ + + +/* * DTrace Activity * * Each DTrace consumer is in one of several states, which (for purposes of