diff --git a/usr/src/cmd/dtrace/test/tst/common/privs/tst.noprivdrop.ksh b/usr/src/cmd/dtrace/test/tst/common/privs/tst.noprivdrop.ksh
new file mode 100755
index 0000000..a5cd183
--- /dev/null
+++ b/usr/src/cmd/dtrace/test/tst/common/privs/tst.noprivdrop.ksh
@@ -0,0 +1,72 @@
+#
+# 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 (c) 2011, Joyent, Inc. All rights reserved.
+#
+
+ppriv -s A=basic,dtrace_user $$
+
+#
+# We expect some number of these profile probes to be silently dropped.
+# Note that this test will fail if something is stuck on all CPUs that
+# whomever is running the test happens to own.
+#
+count=$(/usr/sbin/dtrace -q -s /dev/stdin <<EOF
+BEGIN
+{
+	start = timestamp;
+	@ = count();
+}
+
+ERROR
+{
+	exit(1);
+}
+
+profile-1000hz
+{
+	@ = count();
+}
+
+tick-10ms
+{
+	ticks++;
+}
+
+tick-10ms
+/ticks > 100/
+{
+	printa("%@d", @);
+	exit(0);
+}
+EOF)
+
+cpus=`psrinfo | grep -- on-line | wc -l`
+max=`expr $cpus \* 500`
+
+if [[ $count -gt $max ]]; then
+	echo "count ($count) is greater than allowed max ($max)"
+	exit 1
+fi
+
+echo "count ($count) is within allowed max ($max)"
+exit 0
diff --git a/usr/src/cmd/dtrace/test/tst/common/privs/tst.noprivrestrict.ksh b/usr/src/cmd/dtrace/test/tst/common/privs/tst.noprivrestrict.ksh
new file mode 100755
index 0000000..358ed92
--- /dev/null
+++ b/usr/src/cmd/dtrace/test/tst/common/privs/tst.noprivrestrict.ksh
@@ -0,0 +1,61 @@
+#
+# 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 (c) 2011, Joyent, Inc. All rights reserved.
+#
+
+ppriv -s A=basic,dtrace_user $$
+
+#
+# We expect at least one of these tick probes to error out because only
+# dtrace_user is set, and we are attempting to access arguments.  Note that
+# this test will fail if something is stuck on CPU that whomever is running
+# the test happens to own.
+#
+/usr/sbin/dtrace -q -s /dev/stdin <<EOF
+BEGIN
+{
+	start = timestamp;
+}
+
+tick-1000hz
+{
+	@[arg0] = count();
+}
+
+ERROR
+{
+	errcnt++;
+}
+
+tick-10ms
+{
+	ticks++;
+}
+
+tick-10ms
+/ticks > 100/
+{
+	printf("error count is %d\n", errcnt);
+	exit(errcnt != 0 ? 0 : 1);
+}
+EOF
diff --git a/usr/src/cmd/dtrace/test/tst/common/privs/tst.tick.ksh b/usr/src/cmd/dtrace/test/tst/common/privs/tst.tick.ksh
new file mode 100755
index 0000000..eaff59f
--- /dev/null
+++ b/usr/src/cmd/dtrace/test/tst/common/privs/tst.tick.ksh
@@ -0,0 +1,55 @@
+#
+# 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 (c) 2011, Joyent, Inc. All rights reserved.
+#
+
+ppriv -s A=basic,dtrace_user $$
+
+#
+# We expect tick probes to fire if dtrace_user is set
+#
+/usr/sbin/dtrace -q -s /dev/stdin <<EOF
+BEGIN
+{
+	start = timestamp;
+}
+
+tick-10ms
+{
+	ticks++;
+}
+
+tick-10ms
+/ticks > 10 && (this->ms = (timestamp - start) / 1000000) > 2000/
+{
+	printf("expected completion in 100 ms, found %d!\n", this->ms);
+	exit(1);
+}
+
+tick-10ms
+/ticks > 10/
+{
+	printf("completed in %d ms\n", this->ms);
+	exit(0);
+}
+EOF
diff --git a/usr/src/uts/common/dtrace/dcpc.c b/usr/src/uts/common/dtrace/dcpc.c
index d4ca1d3..8fd96cc 100644
--- a/usr/src/uts/common/dtrace/dcpc.c
+++ b/usr/src/uts/common/dtrace/dcpc.c
@@ -339,9 +339,13 @@ dcpc_destroy(void *arg, dtrace_id_t id, void *parg)
 
 /*ARGSUSED*/
 static int
-dcpc_usermode(void *arg, dtrace_id_t id, void *parg)
+dcpc_mode(void *arg, dtrace_id_t id, void *parg)
 {
-	return (CPU->cpu_cpcprofile_pc == 0);
+	if (CPU->cpu_cpcprofile_pc == 0) {
+		return (DTRACE_MODE_NOPRIV_DROP | DTRACE_MODE_USER);
+	} else {
+		return (DTRACE_MODE_NOPRIV_DROP | DTRACE_MODE_KERNEL);
+	}
 }
 
 static void
@@ -1013,7 +1017,7 @@ static dtrace_pops_t dcpc_pops = {
     NULL,
     NULL,
     NULL,
-    dcpc_usermode,
+    dcpc_mode,
     dcpc_destroy
 };
 
diff --git a/usr/src/uts/common/dtrace/dtrace.c b/usr/src/uts/common/dtrace/dtrace.c
index c08040c..92b5f57 100644
--- a/usr/src/uts/common/dtrace/dtrace.c
+++ b/usr/src/uts/common/dtrace/dtrace.c
@@ -1108,10 +1108,13 @@ dtrace_priv_proc_common_nocd()
 }
 
 static int
-dtrace_priv_proc_destructive(dtrace_state_t *state)
+dtrace_priv_proc_destructive(dtrace_state_t *state, dtrace_mstate_t *mstate)
 {
 	int action = state->dts_cred.dcr_action;
 
+	if (!(mstate->dtms_access & DTRACE_ACCESS_PROC))
+		goto bad;
+
 	if (((action & DTRACE_CRA_PROC_DESTRUCTIVE_ALLZONE) == 0) &&
 	    dtrace_priv_proc_common_zone(state) == 0)
 		goto bad;
@@ -1133,15 +1136,17 @@ bad:
 }
 
 static int
-dtrace_priv_proc_control(dtrace_state_t *state)
+dtrace_priv_proc_control(dtrace_state_t *state, dtrace_mstate_t *mstate)
 {
-	if (state->dts_cred.dcr_action & DTRACE_CRA_PROC_CONTROL)
-		return (1);
+	if (mstate->dtms_access & DTRACE_ACCESS_PROC) {
+		if (state->dts_cred.dcr_action & DTRACE_CRA_PROC_CONTROL)
+			return (1);
 
-	if (dtrace_priv_proc_common_zone(state) &&
-	    dtrace_priv_proc_common_user(state) &&
-	    dtrace_priv_proc_common_nocd())
-		return (1);
+		if (dtrace_priv_proc_common_zone(state) &&
+		    dtrace_priv_proc_common_user(state) &&
+		    dtrace_priv_proc_common_nocd())
+			return (1);
+	}
 
 	cpu_core[CPU->cpu_id].cpuc_dtrace_flags |= CPU_DTRACE_UPRIV;
 
@@ -1149,9 +1154,10 @@ dtrace_priv_proc_control(dtrace_state_t *state)
 }
 
 static int
-dtrace_priv_proc(dtrace_state_t *state)
+dtrace_priv_proc(dtrace_state_t *state, dtrace_mstate_t *mstate)
 {
-	if (state->dts_cred.dcr_action & DTRACE_CRA_PROC)
+	if ((mstate->dtms_access & DTRACE_ACCESS_PROC) &&
+	    (state->dts_cred.dcr_action & DTRACE_CRA_PROC))
 		return (1);
 
 	cpu_core[CPU->cpu_id].cpuc_dtrace_flags |= CPU_DTRACE_UPRIV;
@@ -1182,6 +1188,109 @@ dtrace_priv_kernel_destructive(dtrace_state_t *state)
 }
 
 /*
+ * Determine if the dte_cond of the specified ECB allows for processing of
+ * the current probe to continue.  Note that this routine may allow continued
+ * processing, but with access(es) stripped from the mstate's dtms_access
+ * field.
+ */
+static int
+dtrace_priv_probe(dtrace_state_t *state, dtrace_mstate_t *mstate,
+    dtrace_ecb_t *ecb)
+{
+	dtrace_probe_t *probe = ecb->dte_probe;
+	dtrace_provider_t *prov = probe->dtpr_provider;
+	dtrace_pops_t *pops = &prov->dtpv_pops;
+	int mode = DTRACE_MODE_NOPRIV_DROP;
+
+	ASSERT(ecb->dte_cond);
+
+	if (pops->dtps_mode != NULL) {
+		mode = pops->dtps_mode(prov->dtpv_arg,
+		    probe->dtpr_id, probe->dtpr_arg);
+
+		ASSERT((mode & DTRACE_MODE_USER) ||
+		    (mode & DTRACE_MODE_KERNEL));
+		ASSERT((mode & DTRACE_MODE_NOPRIV_RESTRICT) ||
+		    (mode & DTRACE_MODE_NOPRIV_DROP));
+	}
+
+	/*
+	 * If the dte_cond bits indicate that this consumer is only allowed to
+	 * see user-mode firings of this probe, call the provider's dtps_mode()
+	 * entry point to check that the probe was fired while in a user
+	 * context.  If that's not the case, use the policy specified by the
+	 * provider to determine if we drop the probe or merely restrict
+	 * operation.
+	 */
+	if (ecb->dte_cond & DTRACE_COND_USERMODE) {
+		ASSERT(mode != DTRACE_MODE_NOPRIV_DROP);
+
+		if (!(mode & DTRACE_MODE_USER)) {
+			if (mode & DTRACE_MODE_NOPRIV_DROP)
+				return (0);
+
+			mstate->dtms_access &= ~DTRACE_ACCESS_ARGS;
+		}
+	}
+
+	/*
+	 * This is more subtle than it looks. We have to be absolutely certain
+	 * that CRED() isn't going to change out from under us so it's only
+	 * legit to examine that structure if we're in constrained situations.
+	 * Currently, the only times we'll this check is if a non-super-user
+	 * has enabled the profile or syscall providers -- providers that
+	 * allow visibility of all processes. For the profile case, the check
+	 * above will ensure that we're examining a user context.
+	 */
+	if (ecb->dte_cond & DTRACE_COND_OWNER) {
+		cred_t *cr;
+		cred_t *s_cr = state->dts_cred.dcr_cred;
+		proc_t *proc;
+
+		ASSERT(s_cr != NULL);
+
+		if ((cr = CRED()) == NULL ||
+		    s_cr->cr_uid != cr->cr_uid ||
+		    s_cr->cr_uid != cr->cr_ruid ||
+		    s_cr->cr_uid != cr->cr_suid ||
+		    s_cr->cr_gid != cr->cr_gid ||
+		    s_cr->cr_gid != cr->cr_rgid ||
+		    s_cr->cr_gid != cr->cr_sgid ||
+		    (proc = ttoproc(curthread)) == NULL ||
+		    (proc->p_flag & SNOCD)) {
+			if (mode & DTRACE_MODE_NOPRIV_DROP)
+				return (0);
+
+			mstate->dtms_access &= ~DTRACE_ACCESS_PROC;
+		}
+	}
+
+	/*
+	 * If our dte_cond is set to DTRACE_COND_ZONEOWNER and we are not
+	 * in our zone, check to see if our mode policy is to restrict rather
+	 * than to drop; if to restrict, strip away both DTRACE_ACCESS_PROC
+	 * and DTRACE_ACCESS_ARGS
+	 */
+	if (ecb->dte_cond & DTRACE_COND_ZONEOWNER) {
+		cred_t *cr;
+		cred_t *s_cr = state->dts_cred.dcr_cred;
+
+		ASSERT(s_cr != NULL);
+
+		if ((cr = CRED()) == NULL ||
+		    s_cr->cr_zone->zone_id != cr->cr_zone->zone_id) {
+			if (mode & DTRACE_MODE_NOPRIV_DROP)
+				return (0);
+
+			mstate->dtms_access &=
+			    ~(DTRACE_ACCESS_PROC | DTRACE_ACCESS_ARGS);
+		}
+	}
+
+	return (1);
+}
+
+/*
  * Note:  not called from probe context.  This function is called
  * asynchronously (and at a regular interval) from outside of probe context to
  * clean the dirty dynamic variable lists on all CPUs.  Dynamic variable
@@ -2713,6 +2822,12 @@ dtrace_dif_variable(dtrace_mstate_t *mstate, dtrace_state_t *state, uint64_t v,
 
 	switch (v) {
 	case DIF_VAR_ARGS:
+		if (!(mstate->dtms_access & DTRACE_ACCESS_ARGS)) {
+			cpu_core[CPU->cpu_id].cpuc_dtrace_flags |=
+			    CPU_DTRACE_KPRIV;
+			return (0);
+		}
+
 		ASSERT(mstate->dtms_present & DTRACE_MSTATE_ARGS);
 		if (ndx >= sizeof (mstate->dtms_arg) /
 		    sizeof (mstate->dtms_arg[0])) {
@@ -2748,7 +2863,7 @@ dtrace_dif_variable(dtrace_mstate_t *mstate, dtrace_state_t *state, uint64_t v,
 	case DIF_VAR_UREGS: {
 		klwp_t *lwp;
 
-		if (!dtrace_priv_proc(state))
+		if (!dtrace_priv_proc(state, mstate))
 			return (0);
 
 		if ((lwp = curthread->t_lwp) == NULL) {
@@ -2828,7 +2943,7 @@ dtrace_dif_variable(dtrace_mstate_t *mstate, dtrace_state_t *state, uint64_t v,
 		return (mstate->dtms_stackdepth);
 
 	case DIF_VAR_USTACKDEPTH:
-		if (!dtrace_priv_proc(state))
+		if (!dtrace_priv_proc(state, mstate))
 			return (0);
 		if (!(mstate->dtms_present & DTRACE_MSTATE_USTACKDEPTH)) {
 			/*
@@ -2883,7 +2998,7 @@ dtrace_dif_variable(dtrace_mstate_t *mstate, dtrace_state_t *state, uint64_t v,
 		return (mstate->dtms_caller);
 
 	case DIF_VAR_UCALLER:
-		if (!dtrace_priv_proc(state))
+		if (!dtrace_priv_proc(state, mstate))
 			return (0);
 
 		if (!(mstate->dtms_present & DTRACE_MSTATE_UCALLER)) {
@@ -2931,7 +3046,7 @@ dtrace_dif_variable(dtrace_mstate_t *mstate, dtrace_state_t *state, uint64_t v,
 		    state, mstate));
 
 	case DIF_VAR_PID:
-		if (!dtrace_priv_proc(state))
+		if (!dtrace_priv_proc(state, mstate))
 			return (0);
 
 		/*
@@ -2953,7 +3068,7 @@ dtrace_dif_variable(dtrace_mstate_t *mstate, dtrace_state_t *state, uint64_t v,
 		return ((uint64_t)curthread->t_procp->p_pidp->pid_id);
 
 	case DIF_VAR_PPID:
-		if (!dtrace_priv_proc(state))
+		if (!dtrace_priv_proc(state, mstate))
 			return (0);
 
 		/*
@@ -2980,7 +3095,7 @@ dtrace_dif_variable(dtrace_mstate_t *mstate, dtrace_state_t *state, uint64_t v,
 		return ((uint64_t)curthread->t_tid);
 
 	case DIF_VAR_EXECNAME:
-		if (!dtrace_priv_proc(state))
+		if (!dtrace_priv_proc(state, mstate))
 			return (0);
 
 		/*
@@ -3000,7 +3115,7 @@ dtrace_dif_variable(dtrace_mstate_t *mstate, dtrace_state_t *state, uint64_t v,
 		    state, mstate));
 
 	case DIF_VAR_ZONENAME:
-		if (!dtrace_priv_proc(state))
+		if (!dtrace_priv_proc(state, mstate))
 			return (0);
 
 		/*
@@ -3020,7 +3135,7 @@ dtrace_dif_variable(dtrace_mstate_t *mstate, dtrace_state_t *state, uint64_t v,
 		    state, mstate));
 
 	case DIF_VAR_UID:
-		if (!dtrace_priv_proc(state))
+		if (!dtrace_priv_proc(state, mstate))
 			return (0);
 
 		/*
@@ -3041,7 +3156,7 @@ dtrace_dif_variable(dtrace_mstate_t *mstate, dtrace_state_t *state, uint64_t v,
 		return ((uint64_t)curthread->t_procp->p_cred->cr_uid);
 
 	case DIF_VAR_GID:
-		if (!dtrace_priv_proc(state))
+		if (!dtrace_priv_proc(state, mstate))
 			return (0);
 
 		/*
@@ -3063,7 +3178,7 @@ dtrace_dif_variable(dtrace_mstate_t *mstate, dtrace_state_t *state, uint64_t v,
 
 	case DIF_VAR_ERRNO: {
 		klwp_t *lwp;
-		if (!dtrace_priv_proc(state))
+		if (!dtrace_priv_proc(state, mstate))
 			return (0);
 
 		/*
@@ -3403,7 +3518,7 @@ dtrace_dif_subr(uint_t subr, uint_t rd, uint64_t *regs,
 		uint64_t size = tupregs[2].dttk_value;
 
 		if (!dtrace_destructive_disallow &&
-		    dtrace_priv_proc_control(state) &&
+		    dtrace_priv_proc_control(state, mstate) &&
 		    !dtrace_istoxic(kaddr, size)) {
 			DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
 			dtrace_copyout(kaddr, uaddr, size, flags);
@@ -3418,7 +3533,7 @@ dtrace_dif_subr(uint_t subr, uint_t rd, uint64_t *regs,
 		uint64_t size = tupregs[2].dttk_value;
 
 		if (!dtrace_destructive_disallow &&
-		    dtrace_priv_proc_control(state) &&
+		    dtrace_priv_proc_control(state, mstate) &&
 		    !dtrace_istoxic(kaddr, size)) {
 			DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
 			dtrace_copyoutstr(kaddr, uaddr, size, flags);
@@ -5722,6 +5837,7 @@ dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1,
 #endif
 
 		mstate.dtms_present = DTRACE_MSTATE_ARGS | DTRACE_MSTATE_PROBE;
+		mstate.dtms_access = DTRACE_ACCESS_ARGS | DTRACE_ACCESS_PROC;
 		*flags &= ~CPU_DTRACE_ERROR;
 
 		if (prov == dtrace_provider) {
@@ -5759,65 +5875,8 @@ dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1,
 			}
 		}
 
-		if (ecb->dte_cond) {
-			/*
-			 * If the dte_cond bits indicate that this
-			 * consumer is only allowed to see user-mode firings
-			 * of this probe, call the provider's dtps_usermode()
-			 * entry point to check that the probe was fired
-			 * while in a user context. Skip this ECB if that's
-			 * not the case.
-			 */
-			if ((ecb->dte_cond & DTRACE_COND_USERMODE) &&
-			    prov->dtpv_pops.dtps_usermode(prov->dtpv_arg,
-			    probe->dtpr_id, probe->dtpr_arg) == 0)
-				continue;
-
-			/*
-			 * This is more subtle than it looks. We have to be
-			 * absolutely certain that CRED() isn't going to
-			 * change out from under us so it's only legit to
-			 * examine that structure if we're in constrained
-			 * situations. Currently, the only times we'll this
-			 * check is if a non-super-user has enabled the
-			 * profile or syscall providers -- providers that
-			 * allow visibility of all processes. For the
-			 * profile case, the check above will ensure that
-			 * we're examining a user context.
-			 */
-			if (ecb->dte_cond & DTRACE_COND_OWNER) {
-				cred_t *cr;
-				cred_t *s_cr =
-				    ecb->dte_state->dts_cred.dcr_cred;
-				proc_t *proc;
-
-				ASSERT(s_cr != NULL);
-
-				if ((cr = CRED()) == NULL ||
-				    s_cr->cr_uid != cr->cr_uid ||
-				    s_cr->cr_uid != cr->cr_ruid ||
-				    s_cr->cr_uid != cr->cr_suid ||
-				    s_cr->cr_gid != cr->cr_gid ||
-				    s_cr->cr_gid != cr->cr_rgid ||
-				    s_cr->cr_gid != cr->cr_sgid ||
-				    (proc = ttoproc(curthread)) == NULL ||
-				    (proc->p_flag & SNOCD))
-					continue;
-			}
-
-			if (ecb->dte_cond & DTRACE_COND_ZONEOWNER) {
-				cred_t *cr;
-				cred_t *s_cr =
-				    ecb->dte_state->dts_cred.dcr_cred;
-
-				ASSERT(s_cr != NULL);
-
-				if ((cr = CRED()) == NULL ||
-				    s_cr->cr_zone->zone_id !=
-				    cr->cr_zone->zone_id)
-					continue;
-			}
-		}
+		if (ecb->dte_cond && !dtrace_priv_probe(state, &mstate, ecb))
+			continue;
 
 		if (now - state->dts_alive > dtrace_deadman_timeout) {
 			/*
@@ -5857,9 +5916,7 @@ dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1,
 		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;
+			mstate.dtms_access |= DTRACE_ACCESS_KERNEL;
 
 		if (pred != NULL) {
 			dtrace_difo_t *dp = pred->dtp_difo;
@@ -5919,7 +5976,8 @@ dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1,
 
 			switch (act->dta_kind) {
 			case DTRACEACT_STOP:
-				if (dtrace_priv_proc_destructive(state))
+				if (dtrace_priv_proc_destructive(state,
+				    &mstate))
 					dtrace_action_stop();
 				continue;
 
@@ -5946,7 +6004,7 @@ dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1,
 
 			case DTRACEACT_JSTACK:
 			case DTRACEACT_USTACK:
-				if (!dtrace_priv_proc(state))
+				if (!dtrace_priv_proc(state, &mstate))
 					continue;
 
 				/*
@@ -6032,7 +6090,8 @@ dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1,
 				continue;
 
 			case DTRACEACT_RAISE:
-				if (dtrace_priv_proc_destructive(state))
+				if (dtrace_priv_proc_destructive(state,
+				    &mstate))
 					dtrace_action_raise(val);
 				continue;
 
@@ -6072,7 +6131,7 @@ dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1,
 			case DTRACEACT_UADDR: {
 				struct pid *pid = curthread->t_procp->p_pidp;
 
-				if (!dtrace_priv_proc(state))
+				if (!dtrace_priv_proc(state, &mstate))
 					continue;
 
 				DTRACE_STORE(uint64_t, tomax,
@@ -7004,9 +7063,9 @@ dtrace_register(const char *name, const dtrace_pattr_t *pap, uint32_t priv,
 
 	if ((priv & DTRACE_PRIV_KERNEL) &&
 	    (priv & (DTRACE_PRIV_USER | DTRACE_PRIV_OWNER)) &&
-	    pops->dtps_usermode == NULL) {
+	    pops->dtps_mode == NULL) {
 		cmn_err(CE_WARN, "failed to register provider '%s': need "
-		    "dtps_usermode() op for given privilege attributes", name);
+		    "dtps_mode() op for given privilege attributes", name);
 		return (EINVAL);
 	}
 
@@ -7105,7 +7164,6 @@ dtrace_unregister(dtrace_provider_id_t id)
 	dtrace_provider_t *prev = NULL;
 	int i, self = 0, noreap = 0;
 	dtrace_probe_t *probe, *first = NULL;
-	hrtime_t when;
 
 	if (old->dtpv_pops.dtps_enable ==
 	    (int (*)(void *, dtrace_id_t, void *))dtrace_enable_nullop) {
diff --git a/usr/src/uts/common/dtrace/profile.c b/usr/src/uts/common/dtrace/profile.c
index c1a2d1f..fc809d3 100644
--- a/usr/src/uts/common/dtrace/profile.c
+++ b/usr/src/uts/common/dtrace/profile.c
@@ -23,6 +23,9 @@
  * Use is subject to license terms.
  */
 
+/*
+ * Copyright (c) 2011, Joyent, Inc. All rights reserved.
+ */
 
 #include <sys/errno.h>
 #include <sys/stat.h>
@@ -408,9 +411,25 @@ profile_disable(void *arg, dtrace_id_t id, void *parg)
 
 /*ARGSUSED*/
 static int
-profile_usermode(void *arg, dtrace_id_t id, void *parg)
+profile_mode(void *arg, dtrace_id_t id, void *parg)
 {
-	return (CPU->cpu_profile_pc == 0);
+	profile_probe_t *prof = parg;
+	int mode;
+
+	if (CPU->cpu_profile_pc != 0) {
+		mode = DTRACE_MODE_KERNEL;
+	} else {
+		mode = DTRACE_MODE_USER;
+	}
+
+	if (prof->prof_kind == PROF_TICK) {
+		mode |= DTRACE_MODE_NOPRIV_RESTRICT;
+	} else {
+		ASSERT(prof->prof_kind == PROF_PROFILE);
+		mode |= DTRACE_MODE_NOPRIV_DROP;
+	}
+
+	return (mode);
 }
 
 static dtrace_pattr_t profile_attr = {
@@ -430,7 +449,7 @@ static dtrace_pops_t profile_pops = {
 	NULL,
 	NULL,
 	NULL,
-	profile_usermode,
+	profile_mode,
 	profile_destroy
 };
 
diff --git a/usr/src/uts/common/sys/dtrace.h b/usr/src/uts/common/sys/dtrace.h
index 0c3523d..f00f92e 100644
--- a/usr/src/uts/common/sys/dtrace.h
+++ b/usr/src/uts/common/sys/dtrace.h
@@ -1353,7 +1353,7 @@ typedef struct dof_helper {
  *   dtps_resume()           <-- Resume specified probe
  *   dtps_getargdesc()       <-- Get the argument description for args[X]
  *   dtps_getargval()        <-- Get the value for an argX or args[X] variable
- *   dtps_usermode()         <-- Find out if the probe was fired in user mode
+ *   dtps_mode()             <-- Return the mode of the fired probe
  *   dtps_destroy()          <-- Destroy all state associated with this probe
  *
  * 1.2  void dtps_provide(void *arg, const dtrace_probedesc_t *spec)
@@ -1602,24 +1602,32 @@ typedef struct dof_helper {
  *   This is called from within dtrace_probe() meaning that interrupts
  *   are disabled. No locks should be taken within this entry point.
  *
- * 1.10  int dtps_usermode(void *arg, dtrace_id_t id, void *parg)
+ * 1.10  int dtps_mode(void *arg, dtrace_id_t id, void *parg)
  *
  * 1.10.1  Overview
  *
- *   Called to determine if the probe was fired in a user context.
+ *   Called to determine the mode of a fired probe.
  *
  * 1.10.2  Arguments and notes
  *
  *   The first argument is the cookie as passed to dtrace_register(). The
- *   second argument is the identifier of the current probe. The third
+ *   second argument is the identifier of the current probe.  The third
  *   argument is the probe argument as passed to dtrace_probe_create().  This
  *   entry point must not be left NULL for providers whose probes allow for
- *   mixed mode tracing, that is to say those probes that can fire during
- *   kernel- _or_ user-mode execution
+ *   mixed mode tracing, that is to say those unanchored probes that can fire
+ *   during kernel- or user-mode execution.
  *
  * 1.10.3  Return value
  *
- *   A boolean value.
+ *   A bitwise OR that encapsulates both the mode (either DTRACE_MODE_KERNEL
+ *   or DTRACE_MODE_USER) and the policy when the privilege of the enabling
+ *   is insufficient for that mode (either DTRACE_MODE_NOPRIV_DROP or
+ *   DTRACE_MODE_NOPRIV_RESTRICT).  If the policy is DTRACE_MODE_NOPRIV_DROP,
+ *   insufficient privilege will result in the probe firing being silently
+ *   ignored for the enabling; if the policy is DTRACE_NODE_NOPRIV_RESTRICT,
+ *   insufficient privilege will not prevent probe processing for the
+ *   enabling, but restrictions will be in place that induce a UPRIV fault
+ *   upon attempt to examine probe arguments or current process state.
  *
  * 1.10.4  Caller's context
  *
@@ -2010,10 +2018,15 @@ typedef struct dtrace_pops {
 	    dtrace_argdesc_t *desc);
 	uint64_t (*dtps_getargval)(void *arg, dtrace_id_t id, void *parg,
 	    int argno, int aframes);
-	int (*dtps_usermode)(void *arg, dtrace_id_t id, void *parg);
+	int (*dtps_mode)(void *arg, dtrace_id_t id, void *parg);
 	void (*dtps_destroy)(void *arg, dtrace_id_t id, void *parg);
 } dtrace_pops_t;
 
+#define	DTRACE_MODE_KERNEL			0x01
+#define	DTRACE_MODE_USER			0x02
+#define	DTRACE_MODE_NOPRIV_DROP			0x10
+#define	DTRACE_MODE_NOPRIV_RESTRICT		0x20
+
 typedef uintptr_t	dtrace_provider_id_t;
 
 extern int dtrace_register(const char *, const dtrace_pattr_t *, uint32_t,
diff --git a/usr/src/uts/common/sys/dtrace_impl.h b/usr/src/uts/common/sys/dtrace_impl.h
index dc89cb1..3bebd0c 100644
--- a/usr/src/uts/common/sys/dtrace_impl.h
+++ b/usr/src/uts/common/sys/dtrace_impl.h
@@ -929,7 +929,8 @@ typedef struct dtrace_mstate {
  * Access flag used by dtrace_mstate.dtms_access.
  */
 #define	DTRACE_ACCESS_KERNEL	0x1		/* the priv to read kmem */
-
+#define	DTRACE_ACCESS_PROC	0x2		/* the priv for proc state */
+#define	DTRACE_ACCESS_ARGS	0x4		/* the priv to examine args */
 
 /*
  * DTrace Activity
