Improve memguard a bit:
- Provide tunable vm.memguard.desc, so one can specify memory type without changing the code and recompiling the kernel. - Allow to use memguard for kernel modules by providing sysctl vm.memguard.desc, which can be changed to short description of memory type before module is loaded. - Move as much memguard code as possible to memguard.c. - Add sysctl node vm.memguard. and move memguard-specific sysctl there. - Add malloc_desc2type() function for finding memory type based on its short description (ks_shortdesc field). - Memory type can be changed (via vm.memguard.desc sysctl) only if it doesn't exist (will be loaded later) or when no memory is allocated yet. If there is allocated memory for the given memory type, return EBUSY. - Implement two ways of memory types comparsion and make safer/slower the default.
This commit is contained in:
parent
855921cec5
commit
d362c40d3a
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=153880
@ -142,12 +142,6 @@ struct {
|
||||
|
||||
static uma_zone_t mt_zone;
|
||||
|
||||
#ifdef DEBUG_MEMGUARD
|
||||
u_int vm_memguard_divisor;
|
||||
SYSCTL_UINT(_vm, OID_AUTO, memguard_divisor, CTLFLAG_RD, &vm_memguard_divisor,
|
||||
0, "(kmem_size/memguard_divisor) == memguard submap size");
|
||||
#endif
|
||||
|
||||
u_int vm_kmem_size;
|
||||
SYSCTL_UINT(_vm, OID_AUTO, kmem_size, CTLFLAG_RD, &vm_kmem_size, 0,
|
||||
"Size of kernel memory");
|
||||
@ -304,8 +298,7 @@ malloc(unsigned long size, struct malloc_type *mtp, int flags)
|
||||
("malloc(M_WAITOK) in interrupt context"));
|
||||
|
||||
#ifdef DEBUG_MEMGUARD
|
||||
/* XXX CHANGEME! */
|
||||
if (mtp == M_SUBPROC)
|
||||
if (memguard_cmp(mtp))
|
||||
return memguard_alloc(size, flags);
|
||||
#endif
|
||||
|
||||
@ -359,8 +352,7 @@ free(void *addr, struct malloc_type *mtp)
|
||||
return;
|
||||
|
||||
#ifdef DEBUG_MEMGUARD
|
||||
/* XXX CHANGEME! */
|
||||
if (mtp == M_SUBPROC) {
|
||||
if (memguard_cmp(mtp)) {
|
||||
memguard_free(addr);
|
||||
return;
|
||||
}
|
||||
@ -423,8 +415,7 @@ realloc(void *addr, unsigned long size, struct malloc_type *mtp, int flags)
|
||||
*/
|
||||
|
||||
#ifdef DEBUG_MEMGUARD
|
||||
/* XXX: CHANGEME! */
|
||||
if (mtp == M_SUBPROC) {
|
||||
if (memguard_cmp(mtp)) {
|
||||
slab = NULL;
|
||||
alloc = size;
|
||||
} else {
|
||||
@ -549,7 +540,7 @@ kmeminit(void *dummy)
|
||||
* scenarios as they occur. It is only used for debugging.
|
||||
*/
|
||||
vm_memguard_divisor = 10;
|
||||
TUNABLE_INT_FETCH("vm.memguard_divisor", &vm_memguard_divisor);
|
||||
TUNABLE_INT_FETCH("vm.memguard.divisor", &vm_memguard_divisor);
|
||||
|
||||
/* Pick a conservative value if provided value sucks. */
|
||||
if ((vm_memguard_divisor <= 0) ||
|
||||
@ -649,6 +640,19 @@ malloc_uninit(void *data)
|
||||
uma_zfree(mt_zone, mtip);
|
||||
}
|
||||
|
||||
struct malloc_type *
|
||||
malloc_desc2type(const char *desc)
|
||||
{
|
||||
struct malloc_type *mtp;
|
||||
|
||||
mtx_assert(&malloc_mtx, MA_OWNED);
|
||||
for (mtp = kmemstatistics; mtp != NULL; mtp = mtp->ks_next) {
|
||||
if (strcmp(mtp->ks_shortdesc, desc) == 0)
|
||||
return (mtp);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
sysctl_kern_malloc(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
|
@ -189,6 +189,8 @@ void *realloc(void *addr, unsigned long size, struct malloc_type *type,
|
||||
int flags);
|
||||
void *reallocf(void *addr, unsigned long size, struct malloc_type *type,
|
||||
int flags);
|
||||
|
||||
struct malloc_type *malloc_desc2type(const char *desc);
|
||||
#endif /* _KERNEL */
|
||||
|
||||
#endif /* !_SYS_MALLOC_H_ */
|
||||
|
@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
@ -59,6 +60,67 @@ __FBSDID("$FreeBSD$");
|
||||
*/
|
||||
#define MAX_PAGES_PER_ITEM 64
|
||||
|
||||
SYSCTL_NODE(_vm, OID_AUTO, memguard, CTLFLAG_RW, NULL, "MemGuard data");
|
||||
/*
|
||||
* The vm_memguard_divisor variable controls how much of kmem_map should be
|
||||
* reserved for MemGuard.
|
||||
*/
|
||||
u_int vm_memguard_divisor;
|
||||
SYSCTL_UINT(_vm_memguard, OID_AUTO, divisor, CTLFLAG_RD, &vm_memguard_divisor,
|
||||
0, "(kmem_size/memguard_divisor) == memguard submap size");
|
||||
|
||||
/*
|
||||
* Short description (ks_shortdesc) of memory type to monitor.
|
||||
*/
|
||||
static char vm_memguard_desc[128] = "";
|
||||
static struct malloc_type *vm_memguard_mtype = NULL;
|
||||
TUNABLE_STR("vm.memguard.desc", vm_memguard_desc, sizeof(vm_memguard_desc));
|
||||
static int
|
||||
memguard_sysctl_desc(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct malloc_type_internal *mtip;
|
||||
struct malloc_type_stats *mtsp;
|
||||
struct malloc_type *mtp;
|
||||
char desc[128];
|
||||
long bytes;
|
||||
int error, i;
|
||||
|
||||
strlcpy(desc, vm_memguard_desc, sizeof(desc));
|
||||
error = sysctl_handle_string(oidp, desc, sizeof(desc), req);
|
||||
if (error != 0 || req->newptr == NULL)
|
||||
return (error);
|
||||
|
||||
/*
|
||||
* We can change memory type when no memory has been allocated for it
|
||||
* or when there is no such memory type yet (ie. it will be loaded with
|
||||
* kernel module).
|
||||
*/
|
||||
bytes = 0;
|
||||
mtx_lock(&malloc_mtx);
|
||||
mtp = malloc_desc2type(desc);
|
||||
if (mtp != NULL) {
|
||||
mtip = mtp->ks_handle;
|
||||
for (i = 0; i < MAXCPU; i++) {
|
||||
mtsp = &mtip->mti_stats[i];
|
||||
bytes += mtsp->mts_memalloced;
|
||||
bytes -= mtsp->mts_memfreed;
|
||||
}
|
||||
}
|
||||
if (bytes > 0)
|
||||
error = EBUSY;
|
||||
else {
|
||||
/*
|
||||
* If mtp is NULL, it will be initialized in memguard_cmp().
|
||||
*/
|
||||
vm_memguard_mtype = mtp;
|
||||
strlcpy(vm_memguard_desc, desc, sizeof(vm_memguard_desc));
|
||||
}
|
||||
mtx_unlock(&malloc_mtx);
|
||||
return (error);
|
||||
}
|
||||
SYSCTL_PROC(_vm_memguard, OID_AUTO, desc, CTLTYPE_STRING | CTLFLAG_RW, 0, 0,
|
||||
memguard_sysctl_desc, "A", "Short description of memory type to monitor");
|
||||
|
||||
/*
|
||||
* Global MemGuard data.
|
||||
*/
|
||||
@ -239,6 +301,34 @@ memguard_free(void *addr)
|
||||
MEMGUARD_CRIT_SECTION_EXIT;
|
||||
}
|
||||
|
||||
int
|
||||
memguard_cmp(struct malloc_type *mtp)
|
||||
{
|
||||
|
||||
#if 1
|
||||
/*
|
||||
* The safest way of comparsion is to always compare short description
|
||||
* string of memory type, but it is also the slowest way.
|
||||
*/
|
||||
return (strcmp(mtp->ks_shortdesc, vm_memguard_desc) == 0);
|
||||
#else
|
||||
/*
|
||||
* If we compare pointers, there are two possible problems:
|
||||
* 1. Memory type was unloaded and new memory type was allocated at the
|
||||
* same address.
|
||||
* 2. Memory type was unloaded and loaded again, but allocated at a
|
||||
* different address.
|
||||
*/
|
||||
if (vm_memguard_mtype != NULL)
|
||||
return (mtp == vm_memguard_mtype);
|
||||
if (strcmp(mtp->ks_shortdesc, vm_memguard_desc) == 0) {
|
||||
vm_memguard_mtype = mtp;
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Guard a page containing specified object (make it read-only so that
|
||||
* future writes to it fail).
|
||||
|
@ -26,6 +26,9 @@
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
extern u_int vm_memguard_divisor;
|
||||
|
||||
void memguard_init(vm_map_t parent_map, unsigned long size);
|
||||
void *memguard_alloc(unsigned long size, int flags);
|
||||
void memguard_free(void *addr);
|
||||
int memguard_cmp(struct malloc_type *mtp);
|
||||
|
Loading…
Reference in New Issue
Block a user