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
This commit is contained in:
parent
693c9516fa
commit
100949103a
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user