Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 30 Sep 2015 05:24:23 +0000 (UTC)
From:      Mark Johnston <markj@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r288415 - in head: cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/privs cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/scalars sys/cddl/contrib/opensolaris/uts/common/dtrace s...
Message-ID:  <201509300524.t8U5ONsG097254@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: markj
Date: Wed Sep 30 05:24:22 2015
New Revision: 288415
URL: https://svnweb.freebsd.org/changeset/base/288415

Log:
  MFV r288408:
  6266 harden dtrace_difo_chunksize() with respect to malicious DIF
  
  illumos/illumos-gate@395c7a3dcfc66b8b671dc4b3c4a2f0ca26449922
  
  Reviewed by: Alex Wilson <alex.wilson@joyent.com>
  Reviewed by: Dan McDonald <danmcd@omniti.com>
  Approved by: Garrett D'Amore <garrett@damore.org>
  Author: Bryan Cantrill <bryan@joyent.com>
  
  MFC after:	1 week

Added:
  head/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/privs/tst.kpriv.ksh
     - copied unchanged from r288408, vendor/illumos/dist/cmd/dtrace/test/tst/common/privs/tst.kpriv.ksh
  head/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/scalars/err.bigglobal.d
     - copied unchanged from r288408, vendor/illumos/dist/cmd/dtrace/test/tst/common/scalars/err.bigglobal.d
  head/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/scalars/err.biglocal.d
     - copied unchanged from r288408, vendor/illumos/dist/cmd/dtrace/test/tst/common/scalars/err.biglocal.d
Modified:
  head/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c
  head/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace_impl.h
Directory Properties:
  head/cddl/contrib/opensolaris/   (props changed)
  head/sys/cddl/contrib/opensolaris/   (props changed)

Copied: head/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/privs/tst.kpriv.ksh (from r288408, vendor/illumos/dist/cmd/dtrace/test/tst/common/privs/tst.kpriv.ksh)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/privs/tst.kpriv.ksh	Wed Sep 30 05:24:22 2015	(r288415, copy of r288408, vendor/illumos/dist/cmd/dtrace/test/tst/common/privs/tst.kpriv.ksh)
@@ -0,0 +1,112 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2015, Joyent, Inc. All rights reserved.
+#
+
+err=/tmp/err.$$
+
+ppriv -s A=basic,dtrace_user $$
+
+#
+# When we lack dtrace_kernel, we expect to not be able to get at kernel memory
+# via any subroutine or other vector.
+#
+#	trace(func((void *)&\`utsname)); }
+/usr/sbin/dtrace -wq -Cs /dev/stdin 2> $err <<EOF
+
+#define FAIL \
+	printf("able to read kernel memory via %s!\n", badsubr); \
+	exit(2);
+
+#define CANTREAD1(func) \
+    BEGIN { badsubr = "func()"; func((void *)&\`utsname); FAIL }
+
+#define CANTREAD2(func, arg1) \
+    BEGIN { badsubr = "func()"; func((void *)&\`utsname, arg1); FAIL }
+
+#define CANTREAD2ARG1(func, arg0) \
+    BEGIN { badsubr = "func() (arg1)"; func(arg0, (void *)&\`utsname); FAIL }
+
+#define CANTREAD3(func, arg1, arg2) \
+    BEGIN { badsubr = "func()"; func((void *)&\`utsname, arg1, arg2); FAIL }
+
+CANTREAD1(mutex_owned)
+CANTREAD1(mutex_owner)
+CANTREAD1(mutex_type_adaptive)
+CANTREAD1(mutex_type_spin)
+CANTREAD1(rw_read_held)
+CANTREAD1(rw_write_held)
+CANTREAD1(rw_iswriter)
+CANTREAD3(bcopy, alloca(1), 1)
+CANTREAD1(msgsize)
+CANTREAD1(msgdsize)
+CANTREAD1(strlen)
+CANTREAD2(strchr, '!')
+CANTREAD2(strrchr, '!')
+CANTREAD2(strstr, "doogle")
+CANTREAD2ARG1(strstr, "doogle")
+CANTREAD2(index, "bagnoogle")
+CANTREAD2ARG1(index, "bagnoogle")
+CANTREAD2(rindex, "bagnoogle")
+CANTREAD2ARG1(rindex, "bagnoogle")
+CANTREAD2(strtok, "doogle")
+CANTREAD2ARG1(strtok, "doogle")
+CANTREAD2(json, "doogle")
+CANTREAD2ARG1(json, "doogle")
+CANTREAD1(toupper)
+CANTREAD1(tolower)
+CANTREAD2(ddi_pathname, 1)
+CANTREAD2(strjoin, "doogle")
+CANTREAD2ARG1(strjoin, "doogle")
+CANTREAD1(strtoll)
+CANTREAD1(dirname)
+CANTREAD1(basename)
+CANTREAD1(cleanpath)
+
+#if defined(__amd64)
+CANTREAD3(copyout, uregs[R_R9], 1)
+CANTREAD3(copyoutstr, uregs[R_R9], 1)
+#else
+#if defined(__i386)
+CANTREAD3(copyout, uregs[R_ESP], 1)
+CANTREAD3(copyoutstr, uregs[R_ESP], 1)
+#endif
+#endif
+
+BEGIN
+{
+	exit(0);
+}
+
+ERROR
+/arg4 != DTRACEFLT_KPRIV/
+{
+	printf("bad error code via %s (expected %d, found %d)\n",
+	    badsubr, DTRACEFLT_KPRIV, arg4);
+	exit(3);
+}
+
+ERROR
+/arg4 == DTRACEFLT_KPRIV/
+{
+	printf("illegal kernel access properly prevented from %s\n", badsubr);
+}
+EOF
+
+status=$?
+
+if [[ $status -eq 1 ]]; then
+	cat $err
+fi
+
+exit $status

Copied: head/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/scalars/err.bigglobal.d (from r288408, vendor/illumos/dist/cmd/dtrace/test/tst/common/scalars/err.bigglobal.d)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/scalars/err.bigglobal.d	Wed Sep 30 05:24:22 2015	(r288415, copy of r288408, vendor/illumos/dist/cmd/dtrace/test/tst/common/scalars/err.bigglobal.d)
@@ -0,0 +1,26 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ */
+
+struct mrbig {
+	char toomany[100000];
+};
+
+struct mrbig mrbig;
+
+BEGIN
+{
+	mrbig.toomany[0] = '!';
+	exit(0);
+}

Copied: head/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/scalars/err.biglocal.d (from r288408, vendor/illumos/dist/cmd/dtrace/test/tst/common/scalars/err.biglocal.d)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/scalars/err.biglocal.d	Wed Sep 30 05:24:22 2015	(r288415, copy of r288408, vendor/illumos/dist/cmd/dtrace/test/tst/common/scalars/err.biglocal.d)
@@ -0,0 +1,26 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ */
+
+struct mrbig {
+	char toomany[100000];
+};
+
+this struct mrbig mrbig;
+
+BEGIN
+{
+	this->mrbig.toomany[0] = '!';
+	exit(0);
+}

Modified: head/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c	Wed Sep 30 05:19:16 2015	(r288414)
+++ head/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c	Wed Sep 30 05:24:22 2015	(r288415)
@@ -155,7 +155,7 @@ int		dtrace_destructive_disallow = 0;
 dtrace_optval_t	dtrace_nonroot_maxsize = (16 * 1024 * 1024);
 size_t		dtrace_difo_maxsize = (256 * 1024);
 dtrace_optval_t	dtrace_dof_maxsize = (8 * 1024 * 1024);
-size_t		dtrace_global_maxsize = (16 * 1024);
+size_t		dtrace_statvar_maxsize = (16 * 1024);
 size_t		dtrace_actions_max = (16 * 1024);
 size_t		dtrace_retain_max = 1024;
 dtrace_optval_t	dtrace_helper_actions_max = 128;
@@ -699,13 +699,33 @@ dtrace_canstore_statvar(uint64_t addr, s
     dtrace_statvar_t **svars, int nsvars)
 {
 	int i;
+	size_t maxglobalsize, maxlocalsize;
+
+	if (nsvars == 0)
+		return (0);
+
+	maxglobalsize = dtrace_statvar_maxsize;
+	maxlocalsize = (maxglobalsize + sizeof (uint64_t)) * NCPU;
 
 	for (i = 0; i < nsvars; i++) {
 		dtrace_statvar_t *svar = svars[i];
+		uint8_t scope;
+		size_t size;
 
-		if (svar == NULL || svar->dtsv_size == 0)
+		if (svar == NULL || (size = svar->dtsv_size) == 0)
 			continue;
 
+		scope = svar->dtsv_var.dtdv_scope;
+
+		/*
+		 * We verify that our size is valid in the spirit of providing
+		 * defense in depth:  we want to prevent attackers from using
+		 * DTrace to escalate an orthogonal kernel heap corruption bug
+		 * into the ability to store to arbitrary locations in memory.
+		 */
+		VERIFY((scope == DIFV_SCOPE_GLOBAL && size < maxglobalsize) ||
+		    (scope == DIFV_SCOPE_LOCAL && size < maxlocalsize));
+
 		if (DTRACE_INRANGE(addr, sz, svar->dtsv_data, svar->dtsv_size))
 			return (1);
 	}
@@ -4455,7 +4475,8 @@ dtrace_dif_subr(uint_t subr, uint_t rd, 
 
 		if (!dtrace_destructive_disallow &&
 		    dtrace_priv_proc_control(state) &&
-		    !dtrace_istoxic(kaddr, size)) {
+		    !dtrace_istoxic(kaddr, size) &&
+		    dtrace_canload(kaddr, size, mstate, vstate)) {
 			DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
 			dtrace_copyout(kaddr, uaddr, size, flags);
 			DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
@@ -4470,7 +4491,8 @@ dtrace_dif_subr(uint_t subr, uint_t rd, 
 
 		if (!dtrace_destructive_disallow &&
 		    dtrace_priv_proc_control(state) &&
-		    !dtrace_istoxic(kaddr, size)) {
+		    !dtrace_istoxic(kaddr, size) &&
+		    dtrace_strcanload(kaddr, size, mstate, vstate)) {
 			DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
 			dtrace_copyoutstr(kaddr, uaddr, size, flags);
 			DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
@@ -6458,6 +6480,11 @@ dtrace_dif_emulate(dtrace_difo_t *difo, 
 				    regs[r2] ? regs[r2] :
 				    dtrace_strsize_default) + 1;
 			} else {
+				if (regs[r2] > LONG_MAX) {
+					*flags |= CPU_DTRACE_ILLOP;
+					break;
+				}
+
 				tupregs[ttop].dttk_size = regs[r2];
 			}
 
@@ -9915,9 +9942,10 @@ dtrace_difo_validate(dtrace_difo_t *dp, 
 				break;
 			}
 
-			if (v->dtdv_scope == DIFV_SCOPE_GLOBAL &&
-			    vt->dtdt_size > dtrace_global_maxsize) {
-				err += efunc(i, "oversized by-ref global\n");
+			if ((v->dtdv_scope == DIFV_SCOPE_GLOBAL ||
+			    v->dtdv_scope == DIFV_SCOPE_LOCAL) &&
+			    vt->dtdt_size > dtrace_statvar_maxsize) {
+				err += efunc(i, "oversized by-ref static\n");
 				break;
 			}
 		}
@@ -10261,6 +10289,9 @@ dtrace_difo_chunksize(dtrace_difo_t *dp,
 				if (srd == 0)
 					return;
 
+				if (sval > LONG_MAX)
+					return;
+
 				tupregs[ttop++].dttk_size = sval;
 			}
 
@@ -10322,6 +10353,19 @@ dtrace_difo_chunksize(dtrace_difo_t *dp,
 		 */
 		size = P2ROUNDUP(size, sizeof (uint64_t));
 
+		/*
+		 * Before setting the chunk size, check that we're not going
+		 * to set it to a negative value...
+		 */
+		if (size > LONG_MAX)
+			return;
+
+		/*
+		 * ...and make certain that we didn't badly overflow.
+		 */
+		if (size < ksize || size < sizeof (dtrace_dynvar_t))
+			return;
+
 		if (size > vstate->dtvs_dynvars.dtds_chunksize)
 			vstate->dtvs_dynvars.dtds_chunksize = size;
 	}
@@ -13942,6 +13986,8 @@ dtrace_dstate_init(dtrace_dstate_t *dsta
 	if ((dstate->dtds_chunksize = chunksize) == 0)
 		dstate->dtds_chunksize = DTRACE_DYNVAR_CHUNKSIZE;
 
+	VERIFY(dstate->dtds_chunksize < LONG_MAX);
+
 	if (size < (min = dstate->dtds_chunksize + sizeof (dtrace_dynhash_t)))
 		size = min;
 
@@ -13982,6 +14028,9 @@ dtrace_dstate_init(dtrace_dstate_t *dsta
 	    ((uintptr_t)base + hashsize * sizeof (dtrace_dynhash_t));
 	limit = (uintptr_t)base + size;
 
+	VERIFY((uintptr_t)start < limit);
+	VERIFY((uintptr_t)start >= (uintptr_t)base);
+
 	maxper = (limit - (uintptr_t)start) / NCPU;
 	maxper = (maxper / dstate->dtds_chunksize) * dstate->dtds_chunksize;
 
@@ -14007,7 +14056,7 @@ dtrace_dstate_init(dtrace_dstate_t *dsta
 			start = (dtrace_dynvar_t *)limit;
 		}
 
-		ASSERT(limit <= (uintptr_t)base + size);
+		VERIFY(limit <= (uintptr_t)base + size);
 
 		for (;;) {
 			next = (dtrace_dynvar_t *)((uintptr_t)dvar +
@@ -14016,6 +14065,8 @@ dtrace_dstate_init(dtrace_dstate_t *dsta
 			if ((uintptr_t)next + dstate->dtds_chunksize >= limit)
 				break;
 
+			VERIFY((uintptr_t)dvar >= (uintptr_t)base &&
+			    (uintptr_t)dvar <= (uintptr_t)base + size);
 			dvar->dtdv_next = next;
 			dvar = next;
 		}

Modified: head/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace_impl.h
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace_impl.h	Wed Sep 30 05:19:16 2015	(r288414)
+++ head/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace_impl.h	Wed Sep 30 05:24:22 2015	(r288415)
@@ -1317,16 +1317,19 @@ extern void dtrace_copystr(uintptr_t, ui
 /*
  * DTrace Assertions
  *
- * DTrace calls ASSERT from probe context.  To assure that a failed ASSERT
- * does not induce a markedly more catastrophic failure (e.g., one from which
- * a dump cannot be gleaned), DTrace must define its own ASSERT to be one that
- * may safely be called from probe context.  This header file must thus be
- * included by any DTrace component that calls ASSERT from probe context, and
- * _only_ by those components.  (The only exception to this is kernel
- * debugging infrastructure at user-level that doesn't depend on calling
- * ASSERT.)
+ * DTrace calls ASSERT and VERIFY from probe context.  To assure that a failed
+ * ASSERT or VERIFY does not induce a markedly more catastrophic failure (e.g.,
+ * one from which a dump cannot be gleaned), DTrace must define its own ASSERT
+ * and VERIFY macros to be ones that may safely be called from probe context.
+ * This header file must thus be included by any DTrace component that calls
+ * ASSERT and/or VERIFY from probe context, and _only_ by those components.
+ * (The only exception to this is kernel debugging infrastructure at user-level
+ * that doesn't depend on calling ASSERT.)
  */
 #undef ASSERT
+#undef VERIFY
+#define	VERIFY(EX)	((void)((EX) || \
+			dtrace_assfail(#EX, __FILE__, __LINE__)))
 #ifdef DEBUG
 #define	ASSERT(EX)	((void)((EX) || \
 			dtrace_assfail(#EX, __FILE__, __LINE__)))



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201509300524.t8U5ONsG097254>