git: 100949103a63 - main - uma: Add KMSAN hooks
Mark Johnston
markj at FreeBSD.org
Wed Aug 11 01:31:13 UTC 2021
The branch main has been updated by markj:
URL: https://cgit.FreeBSD.org/src/commit/?id=100949103a6340aff8a6a4caf1927374c242721c
commit 100949103a6340aff8a6a4caf1927374c242721c
Author: Mark Johnston <markj at FreeBSD.org>
AuthorDate: 2021-08-10 21:15:03 +0000
Commit: Mark Johnston <markj at FreeBSD.org>
CommitDate: 2021-08-11 01:27:54 +0000
uma: Add KMSAN hooks
For now, just hook the allocation path: upon allocation, items are
marked as initialized (absent M_ZERO). Some zones are exempted from
this when it would otherwise raise false positives.
Use kmsan_orig() to update the origin map for UMA and malloc(9)
allocations. This allows KMSAN to print the return address when an
uninitialized UMA item is implicated in a report. For example:
panic: MSan: Uninitialized UMA memory from m_getm2+0x7fe
Sponsored by: The FreeBSD Foundation
---
sys/kern/kern_malloc.c | 16 +++++++++++--
sys/kern/kern_mbuf.c | 10 ++++----
sys/vm/uma_core.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++---
sys/vm/vm_glue.c | 2 ++
4 files changed, 80 insertions(+), 10 deletions(-)
diff --git a/sys/kern/kern_malloc.c b/sys/kern/kern_malloc.c
index b30139830a1b..0bdce47b37b4 100644
--- a/sys/kern/kern_malloc.c
+++ b/sys/kern/kern_malloc.c
@@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$");
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
+#include <sys/msan.h>
#include <sys/mutex.h>
#include <sys/vmmeter.h>
#include <sys/proc.h>
@@ -654,8 +655,13 @@ void *
indx = kmemsize[size >> KMEM_ZSHIFT];
zone = kmemzones[indx].kz_zone[mtp_get_subzone(mtp)];
va = uma_zalloc(zone, flags);
- if (va != NULL)
+ if (va != NULL) {
size = zone->uz_size;
+ if ((flags & M_ZERO) == 0) {
+ kmsan_mark(va, size, KMSAN_STATE_UNINIT);
+ kmsan_orig(va, size, KMSAN_TYPE_MALLOC, KMSAN_RET_ADDR);
+ }
+ }
malloc_type_zone_allocated(mtp, va == NULL ? 0 : size, indx);
if (__predict_false(va == NULL)) {
KASSERT((flags & M_WAITOK) == 0,
@@ -735,6 +741,12 @@ malloc_domainset(size_t size, struct malloc_type *mtp, struct domainset *ds,
#ifdef KASAN
if (va != NULL)
kasan_mark((void *)va, osize, size, KASAN_MALLOC_REDZONE);
+#endif
+#ifdef KMSAN
+ if ((flags & M_ZERO) == 0) {
+ kmsan_mark(va, size, KMSAN_STATE_UNINIT);
+ kmsan_orig(va, size, KMSAN_TYPE_MALLOC, KMSAN_RET_ADDR);
+ }
#endif
return (va);
}
@@ -1232,7 +1244,7 @@ mallocinit(void *dummy)
for (subzone = 0; subzone < numzones; subzone++) {
kmemzones[indx].kz_zone[subzone] =
uma_zcreate(name, size,
-#if defined(INVARIANTS) && !defined(KASAN)
+#if defined(INVARIANTS) && !defined(KASAN) && !defined(KMSAN)
mtrash_ctor, mtrash_dtor, mtrash_init, mtrash_fini,
#else
NULL, NULL, NULL, NULL,
diff --git a/sys/kern/kern_mbuf.c b/sys/kern/kern_mbuf.c
index c43f086bb83f..ae4aa7d7e96a 100644
--- a/sys/kern/kern_mbuf.c
+++ b/sys/kern/kern_mbuf.c
@@ -683,7 +683,7 @@ mb_dtor_pack(void *mem, int size, void *arg)
KASSERT(m->m_ext.ext_arg2 == NULL, ("%s: ext_arg2 != NULL", __func__));
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__));
-#ifdef INVARIANTS
+#if defined(INVARIANTS) && !defined(KMSAN)
trash_dtor(m->m_ext.ext_buf, MCLBYTES, arg);
#endif
/*
@@ -742,7 +742,7 @@ mb_zinit_pack(void *mem, int size, int how)
m->m_ext.ext_buf == NULL)
return (ENOMEM);
m->m_ext.ext_type = EXT_PACKET; /* Override. */
-#ifdef INVARIANTS
+#if defined(INVARIANTS) && !defined(KMSAN)
trash_init(m->m_ext.ext_buf, MCLBYTES, how);
#endif
return (0);
@@ -758,11 +758,11 @@ mb_zfini_pack(void *mem, int size)
struct mbuf *m;
m = (struct mbuf *)mem;
-#ifdef INVARIANTS
+#if defined(INVARIANTS) && !defined(KMSAN)
trash_fini(m->m_ext.ext_buf, MCLBYTES);
#endif
uma_zfree_arg(zone_clust, m->m_ext.ext_buf, NULL);
-#ifdef INVARIANTS
+#if defined(INVARIANTS) && !defined(KMSAN)
trash_dtor(mem, size, NULL);
#endif
}
@@ -784,7 +784,7 @@ mb_ctor_pack(void *mem, int size, void *arg, int how)
type = args->type;
MPASS((flags & M_NOFREE) == 0);
-#ifdef INVARIANTS
+#if defined(INVARIANTS) && !defined(KMSAN)
trash_ctor(m->m_ext.ext_buf, MCLBYTES, arg, how);
#endif
diff --git a/sys/vm/uma_core.c b/sys/vm/uma_core.c
index 59cc960d3dd9..cdf3d482679e 100644
--- a/sys/vm/uma_core.c
+++ b/sys/vm/uma_core.c
@@ -69,7 +69,7 @@ __FBSDID("$FreeBSD$");
#include <sys/malloc.h>
#include <sys/ktr.h>
#include <sys/lock.h>
-#include <sys/sysctl.h>
+#include <sys/msan.h>
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/random.h>
@@ -79,6 +79,7 @@ __FBSDID("$FreeBSD$");
#include <sys/sleepqueue.h>
#include <sys/smp.h>
#include <sys/smr.h>
+#include <sys/sysctl.h>
#include <sys/taskqueue.h>
#include <sys/vmmeter.h>
@@ -632,6 +633,60 @@ kasan_mark_slab_invalid(uma_keg_t keg __unused, void *mem __unused)
}
#endif /* KASAN */
+#ifdef KMSAN
+static inline void
+kmsan_mark_item_uninitialized(uma_zone_t zone, void *item)
+{
+ void *pcpu_item;
+ size_t sz;
+ int i;
+
+ if ((zone->uz_flags &
+ (UMA_ZFLAG_CACHE | UMA_ZONE_SECONDARY | UMA_ZONE_MALLOC)) != 0) {
+ /*
+ * Cache zones should not be instrumented by default, as UMA
+ * does not have enough information to do so correctly.
+ * Consumers can mark items themselves if it makes sense to do
+ * so.
+ *
+ * Items from secondary zones are initialized by the parent
+ * zone and thus cannot safely be marked by UMA.
+ *
+ * malloc zones are handled directly by malloc(9) and friends,
+ * since they can provide more precise origin tracking.
+ */
+ return;
+ }
+ if (zone->uz_keg->uk_init != NULL) {
+ /*
+ * By definition, initialized items cannot be marked. The
+ * best we can do is mark items from these zones after they
+ * are freed to the keg.
+ */
+ return;
+ }
+
+ sz = zone->uz_size;
+ if ((zone->uz_flags & UMA_ZONE_PCPU) == 0) {
+ kmsan_orig(item, sz, KMSAN_TYPE_UMA, KMSAN_RET_ADDR);
+ kmsan_mark(item, sz, KMSAN_STATE_UNINIT);
+ } else {
+ pcpu_item = zpcpu_base_to_offset(item);
+ for (i = 0; i <= mp_maxid; i++) {
+ kmsan_orig(zpcpu_get_cpu(pcpu_item, i), sz,
+ KMSAN_TYPE_UMA, KMSAN_RET_ADDR);
+ kmsan_mark(zpcpu_get_cpu(pcpu_item, i), sz,
+ KMSAN_STATE_INITED);
+ }
+ }
+}
+#else /* !KMSAN */
+static inline void
+kmsan_mark_item_uninitialized(uma_zone_t zone __unused, void *item __unused)
+{
+}
+#endif /* KMSAN */
+
/*
* Acquire the domain lock and record contention.
*/
@@ -2799,7 +2854,7 @@ zone_ctor(void *mem, int size, void *udata, int flags)
STAILQ_INIT(&zdom->uzd_buckets);
}
-#if defined(INVARIANTS) && !defined(KASAN)
+#if defined(INVARIANTS) && !defined(KASAN) && !defined(KMSAN)
if (arg->uminit == trash_init && arg->fini == trash_fini)
zone->uz_flags |= UMA_ZFLAG_TRASH | UMA_ZFLAG_CTORDTOR;
#elif defined(KASAN)
@@ -3227,7 +3282,7 @@ uma_zcreate(const char *name, size_t size, uma_ctor ctor, uma_dtor dtor,
args.dtor = dtor;
args.uminit = uminit;
args.fini = fini;
-#if defined(INVARIANTS) && !defined(KASAN)
+#if defined(INVARIANTS) && !defined(KASAN) && !defined(KMSAN)
/*
* Inject procedures which check for memory use after free if we are
* allowed to scramble the memory while it is not allocated. This
@@ -3387,6 +3442,7 @@ item_ctor(uma_zone_t zone, int uz_flags, int size, void *udata, int flags,
#endif
kasan_mark_item_valid(zone, item);
+ kmsan_mark_item_uninitialized(zone, item);
#ifdef INVARIANTS
skipdbg = uma_dbg_zskip(zone, item);
diff --git a/sys/vm/vm_glue.c b/sys/vm/vm_glue.c
index 7cfb08246f9e..5e118a7ad86e 100644
--- a/sys/vm/vm_glue.c
+++ b/sys/vm/vm_glue.c
@@ -73,6 +73,7 @@ __FBSDID("$FreeBSD$");
#include <sys/limits.h>
#include <sys/lock.h>
#include <sys/malloc.h>
+#include <sys/msan.h>
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/racct.h>
@@ -387,6 +388,7 @@ vm_thread_new(struct thread *td, int pages)
td->td_kstack = ks;
td->td_kstack_pages = pages;
kasan_mark((void *)ks, ptoa(pages), ptoa(pages), 0);
+ kmsan_mark((void *)ks, ptoa(pages), KMSAN_STATE_UNINIT);
return (1);
}
More information about the dev-commits-src-all
mailing list