Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 27 Nov 2019 19:49:56 +0000 (UTC)
From:      Ryan Libby <rlibby@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r355137 - head/sys/vm
Message-ID:  <201911271949.xARJnuFl084178@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: rlibby
Date: Wed Nov 27 19:49:55 2019
New Revision: 355137
URL: https://svnweb.freebsd.org/changeset/base/355137

Log:
  uma: trash memory when ctor/dtor supplied too
  
  On INVARIANTS kernels, UMA has a use-after-free detection mechanism.
  This mechanism previously required that all of the ctor/dtor/uminit/fini
  arguments to uma_zcreate() be NULL in order to function.  Now, it only
  requires that uminit and fini be NULL; now, the trash ctor and dtor will
  be called in addition to any supplied ctor or dtor.
  
  Also do a little refactoring for readability of the resulting logic.
  
  This enables use-after-free detection for more zones, and will allow for
  simplification of some callers that worked around the previous
  restriction (see kern_mbuf.c).
  
  Reviewed by:	jeff, markj
  Sponsored by:	Dell EMC Isilon
  Differential Revision:	https://reviews.freebsd.org/D20722

Modified:
  head/sys/vm/uma_core.c
  head/sys/vm/uma_int.h

Modified: head/sys/vm/uma_core.c
==============================================================================
--- head/sys/vm/uma_core.c	Wed Nov 27 19:34:33 2019	(r355136)
+++ head/sys/vm/uma_core.c	Wed Nov 27 19:49:55 2019	(r355137)
@@ -1869,6 +1869,11 @@ zone_ctor(void *mem, int size, void *udata, int flags)
 	for (i = 0; i < vm_ndomains; i++)
 		TAILQ_INIT(&zone->uz_domain[i].uzd_buckets);
 
+#ifdef INVARIANTS
+	if (arg->uminit == trash_init && arg->fini == trash_fini)
+		zone->uz_flags |= UMA_ZFLAG_TRASH;
+#endif
+
 	/*
 	 * This is a pure cache zone, no kegs.
 	 */
@@ -2302,14 +2307,17 @@ uma_zcreate(const char *name, size_t size, uma_ctor ct
 	args.fini = fini;
 #ifdef  INVARIANTS
 	/*
-	 * If a zone is being created with an empty constructor and
-	 * destructor, pass UMA constructor/destructor which checks for
-	 * memory use after free.
+	 * Inject procedures which check for memory use after free if we are
+	 * allowed to scramble the memory while it is not allocated.  This
+	 * requires that: UMA is actually able to access the memory, no init
+	 * or fini procedures, no dependency on the initial value of the
+	 * memory, and no (legitimate) use of the memory after free.  Note,
+	 * the ctor and dtor do not need to be empty.
+	 *
+	 * XXX UMA_ZONE_OFFPAGE.
 	 */
 	if ((!(flags & (UMA_ZONE_ZINIT | UMA_ZONE_NOFREE))) &&
-	    ctor == NULL && dtor == NULL && uminit == NULL && fini == NULL) {
-		args.ctor = trash_ctor;
-		args.dtor = trash_dtor;
+	    uminit == NULL && fini == NULL) {
 		args.uminit = trash_init;
 		args.fini = trash_fini;
 	}
@@ -2462,15 +2470,14 @@ static void *
 item_ctor(uma_zone_t zone, void *udata, int flags, void *item)
 {
 #ifdef INVARIANTS
-	int skipdbg;
+	bool skipdbg;
 
 	skipdbg = uma_dbg_zskip(zone, item);
-	if (zone->uz_ctor != NULL &&
-	    (!skipdbg || zone->uz_ctor != trash_ctor ||
-	    zone->uz_dtor != trash_dtor) &&
-#else
-	if (__predict_false(zone->uz_ctor != NULL) &&
+	if (!skipdbg && (zone->uz_flags & UMA_ZFLAG_TRASH) != 0 &&
+	    zone->uz_ctor != trash_ctor)
+		trash_ctor(item, zone->uz_size, udata, flags);
 #endif
+	if (__predict_false(zone->uz_ctor != NULL) &&
 	    zone->uz_ctor(item, zone->uz_size, udata, flags) != 0) {
 		counter_u64_add(zone->uz_fails, 1);
 		zone_free_item(zone, item, udata, SKIP_DTOR | SKIP_CNT);
@@ -2486,6 +2493,31 @@ item_ctor(uma_zone_t zone, void *udata, int flags, voi
 	return (item);
 }
 
+static inline void
+item_dtor(uma_zone_t zone, void *item, void *udata, enum zfreeskip skip)
+{
+#ifdef INVARIANTS
+	bool skipdbg;
+
+	skipdbg = uma_dbg_zskip(zone, item);
+	if (skip == SKIP_NONE && !skipdbg) {
+		if ((zone->uz_flags & UMA_ZONE_MALLOC) != 0)
+			uma_dbg_free(zone, udata, item);
+		else
+			uma_dbg_free(zone, NULL, item);
+	}
+#endif
+	if (skip < SKIP_DTOR) {
+		if (zone->uz_dtor != NULL)
+			zone->uz_dtor(item, zone->uz_size, udata);
+#ifdef INVARIANTS
+		if (!skipdbg && (zone->uz_flags & UMA_ZFLAG_TRASH) != 0 &&
+		    zone->uz_dtor != trash_dtor)
+			trash_dtor(item, zone->uz_size, udata);
+#endif
+	}
+}
+
 /* See uma.h */
 void *
 uma_zalloc_arg(uma_zone_t zone, void *udata, int flags)
@@ -2523,6 +2555,7 @@ uma_zalloc_arg(uma_zone_t zone, void *udata, int flags
 			if (zone->uz_ctor != NULL &&
 			    zone->uz_ctor(item, zone->uz_size, udata,
 			    flags) != 0) {
+				counter_u64_add(zone->uz_fails, 1);
 			    	zone->uz_fini(item, zone->uz_size);
 				return (NULL);
 			}
@@ -3131,9 +3164,6 @@ uma_zfree_arg(uma_zone_t zone, void *item, void *udata
 	int itemdomain;
 #endif
 	bool lockfail;
-#ifdef INVARIANTS
-	bool skipdbg;
-#endif
 
 	/* Enable entropy collection for RANDOM_ENABLE_UMA kernel option */
 	random_harvest_fast_uma(&zone, sizeof(zone), RANDOM_UMA);
@@ -3157,20 +3187,7 @@ uma_zfree_arg(uma_zone_t zone, void *item, void *udata
 		return;
 	}
 #endif
-#ifdef INVARIANTS
-	skipdbg = uma_dbg_zskip(zone, item);
-	if (skipdbg == false) {
-		if (zone->uz_flags & UMA_ZONE_MALLOC)
-			uma_dbg_free(zone, udata, item);
-		else
-			uma_dbg_free(zone, NULL, item);
-	}
-	if (zone->uz_dtor != NULL && (!skipdbg ||
-	    zone->uz_dtor != trash_dtor || zone->uz_ctor != trash_ctor))
-#else
-	if (zone->uz_dtor != NULL)
-#endif
-		zone->uz_dtor(item, zone->uz_size, udata);
+	item_dtor(zone, item, udata, SKIP_NONE);
 
 	/*
 	 * The race here is acceptable.  If we miss it we'll just have to wait
@@ -3459,24 +3476,8 @@ zone_release(uma_zone_t zone, void **bucket, int cnt)
 static void
 zone_free_item(uma_zone_t zone, void *item, void *udata, enum zfreeskip skip)
 {
-#ifdef INVARIANTS
-	bool skipdbg;
 
-	skipdbg = uma_dbg_zskip(zone, item);
-	if (skip == SKIP_NONE && !skipdbg) {
-		if (zone->uz_flags & UMA_ZONE_MALLOC)
-			uma_dbg_free(zone, udata, item);
-		else
-			uma_dbg_free(zone, NULL, item);
-	}
-
-	if (skip < SKIP_DTOR && zone->uz_dtor != NULL &&
-	    (!skipdbg || zone->uz_dtor != trash_dtor ||
-	    zone->uz_ctor != trash_ctor))
-#else
-	if (skip < SKIP_DTOR && zone->uz_dtor != NULL)
-#endif
-		zone->uz_dtor(item, zone->uz_size, udata);
+	item_dtor(zone, item, udata, skip);
 
 	if (skip < SKIP_FINI && zone->uz_fini)
 		zone->uz_fini(item, zone->uz_size);

Modified: head/sys/vm/uma_int.h
==============================================================================
--- head/sys/vm/uma_int.h	Wed Nov 27 19:34:33 2019	(r355136)
+++ head/sys/vm/uma_int.h	Wed Nov 27 19:49:55 2019	(r355137)
@@ -389,6 +389,7 @@ struct uma_zone {
 #define	UMA_ZFLAG_RECLAIMING	0x08000000	/* Running zone_reclaim(). */
 #define	UMA_ZFLAG_BUCKET	0x10000000	/* Bucket zone. */
 #define UMA_ZFLAG_INTERNAL	0x20000000	/* No offpage no PCPU. */
+#define UMA_ZFLAG_TRASH		0x40000000	/* Add trash ctor/dtor. */
 #define UMA_ZFLAG_CACHEONLY	0x80000000	/* Don't ask VM for buckets. */
 
 #define	UMA_ZFLAG_INHERIT						\



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