git: a03c23931eec - main - uma: Improve memory modified after free panic messages

From: Alexander Motin <mav_at_FreeBSD.org>
Date: Fri, 10 Nov 2023 00:57:04 UTC
The branch main has been updated by mav:

URL: https://cgit.FreeBSD.org/src/commit/?id=a03c23931eec567b0957c2a0b1102dba8d538d98

commit a03c23931eec567b0957c2a0b1102dba8d538d98
Author:     Alexander Motin <mav@FreeBSD.org>
AuthorDate: 2023-11-10 00:46:26 +0000
Commit:     Alexander Motin <mav@FreeBSD.org>
CommitDate: 2023-11-10 00:46:26 +0000

    uma: Improve memory modified after free panic messages
    
     - Pass zone pointer to trash_ctor() and report zone name in the panic
    message.  It may be difficult to figyre out zone just by the item size.
     - Do not pass user arguments to internal trash calls, pass thezone.
     - Report malloc type name in the same unified panic message.
     - Report corruption offset from the beginning of the items instead of
    the full pointer.  It makes panic message shorter and more readable.
---
 sys/kern/kern_malloc.c |  4 +--
 sys/kern/kern_mbuf.c   |  6 ++---
 sys/vm/uma_core.c      |  4 +--
 sys/vm/uma_dbg.c       | 67 ++++++++++++++++++++++++++++++++++----------------
 4 files changed, 53 insertions(+), 28 deletions(-)

diff --git a/sys/kern/kern_malloc.c b/sys/kern/kern_malloc.c
index 07f59f733e6a..da7408edd186 100644
--- a/sys/kern/kern_malloc.c
+++ b/sys/kern/kern_malloc.c
@@ -650,7 +650,7 @@ void *
 		size = (size & ~KMEM_ZMASK) + KMEM_ZBASE;
 	indx = kmemsize[size >> KMEM_ZSHIFT];
 	zone = kmemzones[indx].kz_zone[mtp_get_subzone(mtp)];
-	va = uma_zalloc(zone, flags);
+	va = uma_zalloc_arg(zone, zone, flags);
 	if (va != NULL) {
 		size = zone->uz_size;
 		if ((flags & M_ZERO) == 0) {
@@ -690,7 +690,7 @@ malloc_domain(size_t *sizep, int *indxp, struct malloc_type *mtp, int domain,
 		size = (size & ~KMEM_ZMASK) + KMEM_ZBASE;
 	indx = kmemsize[size >> KMEM_ZSHIFT];
 	zone = kmemzones[indx].kz_zone[mtp_get_subzone(mtp)];
-	va = uma_zalloc_domain(zone, NULL, domain, flags);
+	va = uma_zalloc_domain(zone, zone, domain, flags);
 	if (va != NULL)
 		*sizep = zone->uz_size;
 	*indxp = indx;
diff --git a/sys/kern/kern_mbuf.c b/sys/kern/kern_mbuf.c
index 0897ac4cc2db..60c638735ec4 100644
--- a/sys/kern/kern_mbuf.c
+++ b/sys/kern/kern_mbuf.c
@@ -703,7 +703,7 @@ mb_dtor_pack(void *mem, int size, void *arg)
 	KASSERT(m->m_ext.ext_size == MCLBYTES, ("%s: ext_size != MCLBYTES", __func__));
 	KASSERT(m->m_ext.ext_type == EXT_PACKET, ("%s: ext_type != EXT_PACKET", __func__));
 #if defined(INVARIANTS) && !defined(KMSAN)
-	trash_dtor(m->m_ext.ext_buf, MCLBYTES, arg);
+	trash_dtor(m->m_ext.ext_buf, MCLBYTES, zone_clust);
 #endif
 	/*
 	 * If there are processes blocked on zone_clust, waiting for pages
@@ -782,7 +782,7 @@ mb_zfini_pack(void *mem, int size)
 #endif
 	uma_zfree_arg(zone_clust, m->m_ext.ext_buf, NULL);
 #if defined(INVARIANTS) && !defined(KMSAN)
-	trash_dtor(mem, size, NULL);
+	trash_dtor(mem, size, zone_clust);
 #endif
 }
 
@@ -804,7 +804,7 @@ mb_ctor_pack(void *mem, int size, void *arg, int how)
 	MPASS((flags & M_NOFREE) == 0);
 
 #if defined(INVARIANTS) && !defined(KMSAN)
-	trash_ctor(m->m_ext.ext_buf, MCLBYTES, arg, how);
+	trash_ctor(m->m_ext.ext_buf, MCLBYTES, zone_clust, how);
 #endif
 
 	error = m_init(m, how, type, flags);
diff --git a/sys/vm/uma_core.c b/sys/vm/uma_core.c
index 661c98b272da..d185f12448ee 100644
--- a/sys/vm/uma_core.c
+++ b/sys/vm/uma_core.c
@@ -3468,7 +3468,7 @@ item_ctor(uma_zone_t zone, int uz_flags, int size, void *udata, int flags,
 	skipdbg = uma_dbg_zskip(zone, item);
 	if (!skipdbg && (uz_flags & UMA_ZFLAG_TRASH) != 0 &&
 	    zone->uz_ctor != trash_ctor)
-		trash_ctor(item, size, udata, flags);
+		trash_ctor(item, size, zone, flags);
 #endif
 
 	/* Check flags before loading ctor pointer. */
@@ -3510,7 +3510,7 @@ item_dtor(uma_zone_t zone, void *item, int size, void *udata,
 #ifdef INVARIANTS
 		if (!skipdbg && (zone->uz_flags & UMA_ZFLAG_TRASH) != 0 &&
 		    zone->uz_dtor != trash_dtor)
-			trash_dtor(item, size, udata);
+			trash_dtor(item, size, zone);
 #endif
 	}
 	kasan_mark_item_invalid(zone, item);
diff --git a/sys/vm/uma_dbg.c b/sys/vm/uma_dbg.c
index 76dd2bfde2fe..c256e62875c0 100644
--- a/sys/vm/uma_dbg.c
+++ b/sys/vm/uma_dbg.c
@@ -53,18 +53,22 @@
 #include <vm/uma_dbg.h>
 #include <vm/memguard.h>
 
+#include <machine/stack.h>
+
 static const u_long uma_junk = (u_long)0xdeadc0dedeadc0de;
 
 /*
  * Checks an item to make sure it hasn't been overwritten since it was freed,
  * prior to subsequent reallocation.
  *
- * Complies with standard ctor arg/return
+ * Complies with standard ctor arg/return.  arg should be zone pointer or NULL.
  */
 int
 trash_ctor(void *mem, int size, void *arg, int flags)
 {
+	struct uma_zone *zone = arg;
 	u_long *p = mem, *e;
+	int off;
 
 #ifdef DEBUG_MEMGUARD
 	if (is_memguard_addr(mem))
@@ -73,19 +77,22 @@ trash_ctor(void *mem, int size, void *arg, int flags)
 
 	e = p + size / sizeof(*p);
 	for (; p < e; p++) {
-		if (__predict_true(*p == uma_junk))
-			continue;
-		panic("Memory modified after free %p(%d) val=%lx @ %p\n",
-		    mem, size, *p, p);
+		if (__predict_false(*p != uma_junk))
+			goto dopanic;
 	}
 	return (0);
+
+dopanic:
+	off = (uintptr_t)p - (uintptr_t)mem;
+	panic("Memory modified after free %p (%d, %s) + %d = %lx\n",
+	    mem, size, zone ? zone->uz_name : "", off,  *p);
+	return (0);
 }
 
 /*
  * Fills an item with predictable garbage
  *
  * Complies with standard dtor arg/return
- *
  */
 void
 trash_dtor(void *mem, int size, void *arg)
@@ -106,7 +113,6 @@ trash_dtor(void *mem, int size, void *arg)
  * Fills an item with predictable garbage
  *
  * Complies with standard init arg/return
- *
  */
 int
 trash_init(void *mem, int size, int flags)
@@ -116,10 +122,10 @@ trash_init(void *mem, int size, int flags)
 }
 
 /*
- * Checks an item to make sure it hasn't been overwritten since it was freed.
+ * Checks an item to make sure it hasn't been overwritten since it was freed,
+ * prior to freeing it back to available memory.
  *
  * Complies with standard fini arg/return
- *
  */
 void
 trash_fini(void *mem, int size)
@@ -127,11 +133,19 @@ trash_fini(void *mem, int size)
 	(void)trash_ctor(mem, size, NULL, 0);
 }
 
+/*
+ * Checks an item to make sure it hasn't been overwritten since it was freed,
+ * prior to subsequent reallocation.
+ *
+ * Complies with standard ctor arg/return.  arg should be zone pointer or NULL.
+ */
 int
 mtrash_ctor(void *mem, int size, void *arg, int flags)
 {
-	struct malloc_type **ksp;
+	struct uma_zone *zone = arg;
 	u_long *p = mem, *e;
+	struct malloc_type **ksp;
+	int off, osize = size;
 
 #ifdef DEBUG_MEMGUARD
 	if (is_memguard_addr(mem))
@@ -139,17 +153,31 @@ mtrash_ctor(void *mem, int size, void *arg, int flags)
 #endif
 
 	size -= sizeof(struct malloc_type *);
-	ksp = (struct malloc_type **)mem;
-	ksp += size / sizeof(struct malloc_type *);
 
 	e = p + size / sizeof(*p);
 	for (; p < e; p++) {
-		if (__predict_true(*p == uma_junk))
-			continue;
-		printf("Memory modified after free %p(%d) val=%lx @ %p\n",
-		    mem, size, *p, p);
-		panic("Most recently used by %s\n", (*ksp == NULL)?
-		    "none" : (*ksp)->ks_shortdesc);
+		if (__predict_false(*p != uma_junk))
+			goto dopanic;
+	}
+	return (0);
+
+dopanic:
+	off = (uintptr_t)p - (uintptr_t)mem;
+	ksp = (struct malloc_type **)mem;
+	ksp += size / sizeof(struct malloc_type *);
+	if (*ksp != NULL && INKERNEL((uintptr_t)*ksp)) {
+		/*
+		 * If *ksp is corrupted we may be unable to panic clean,
+		 * so print what we have reliably while we still can.
+		 */
+		printf("Memory modified after free %p (%d, %s, %p) + %d = %lx\n",
+		    mem, osize, zone ? zone->uz_name : "", *ksp, off, *p);
+		panic("Memory modified after free %p (%d, %s, %s) + %d = %lx\n",
+		    mem, osize, zone ? zone->uz_name : "", (*ksp)->ks_shortdesc,
+		    off, *p);
+	} else {
+		panic("Memory modified after free %p (%d, %s, %p) + %d = %lx\n",
+		    mem, osize, zone ? zone->uz_name : "", *ksp, off, *p);
 	}
 	return (0);
 }
@@ -158,7 +186,6 @@ mtrash_ctor(void *mem, int size, void *arg, int flags)
  * Fills an item with predictable garbage
  *
  * Complies with standard dtor arg/return
- *
  */
 void
 mtrash_dtor(void *mem, int size, void *arg)
@@ -181,7 +208,6 @@ mtrash_dtor(void *mem, int size, void *arg)
  * Fills an item with predictable garbage
  *
  * Complies with standard init arg/return
- *
  */
 int
 mtrash_init(void *mem, int size, int flags)
@@ -206,7 +232,6 @@ mtrash_init(void *mem, int size, int flags)
  * prior to freeing it back to available memory.
  *
  * Complies with standard fini arg/return
- *
  */
 void
 mtrash_fini(void *mem, int size)