1e4eb384bSBosko Milekic /* 2e4eb384bSBosko Milekic * Copyright (c) 2005, 3e4eb384bSBosko Milekic * Bosko Milekic <bmilekic@freebsd.org> 4e4eb384bSBosko Milekic * 5e4eb384bSBosko Milekic * Redistribution and use in source and binary forms, with or without 6e4eb384bSBosko Milekic * modification, are permitted provided that the following conditions 7e4eb384bSBosko Milekic * are met: 8e4eb384bSBosko Milekic * 1. Redistributions of source code must retain the above copyright 9e4eb384bSBosko Milekic * notice unmodified, this list of conditions, and the following 10e4eb384bSBosko Milekic * disclaimer. 11e4eb384bSBosko Milekic * 2. Redistributions in binary form must reproduce the above copyright 12e4eb384bSBosko Milekic * notice, this list of conditions and the following disclaimer in the 13e4eb384bSBosko Milekic * documentation and/or other materials provided with the distribution. 14e4eb384bSBosko Milekic * 15e4eb384bSBosko Milekic * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16e4eb384bSBosko Milekic * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17e4eb384bSBosko Milekic * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18e4eb384bSBosko Milekic * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19e4eb384bSBosko Milekic * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20e4eb384bSBosko Milekic * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21e4eb384bSBosko Milekic * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22e4eb384bSBosko Milekic * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23e4eb384bSBosko Milekic * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24e4eb384bSBosko Milekic * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25e4eb384bSBosko Milekic */ 26e4eb384bSBosko Milekic 27e4eb384bSBosko Milekic #include <sys/cdefs.h> 28e4eb384bSBosko Milekic __FBSDID("$FreeBSD$"); 29e4eb384bSBosko Milekic 30e4eb384bSBosko Milekic /* 31e4eb384bSBosko Milekic * MemGuard is a simple replacement allocator for debugging only 32e4eb384bSBosko Milekic * which provides ElectricFence-style memory barrier protection on 33e4eb384bSBosko Milekic * objects being allocated, and is used to detect tampering-after-free 34e4eb384bSBosko Milekic * scenarios. 35e4eb384bSBosko Milekic * 36e4eb384bSBosko Milekic * See the memguard(9) man page for more information on using MemGuard. 37e4eb384bSBosko Milekic */ 38e4eb384bSBosko Milekic 39e4eb384bSBosko Milekic #include <sys/param.h> 40e4eb384bSBosko Milekic #include <sys/systm.h> 41e4eb384bSBosko Milekic #include <sys/kernel.h> 42e4eb384bSBosko Milekic #include <sys/types.h> 43e4eb384bSBosko Milekic #include <sys/queue.h> 44e4eb384bSBosko Milekic #include <sys/lock.h> 45e4eb384bSBosko Milekic #include <sys/mutex.h> 46e4eb384bSBosko Milekic #include <sys/malloc.h> 47e4eb384bSBosko Milekic 48e4eb384bSBosko Milekic #include <vm/vm.h> 49e4eb384bSBosko Milekic #include <vm/vm_page.h> 50e4eb384bSBosko Milekic #include <vm/vm_map.h> 51e4eb384bSBosko Milekic #include <vm/vm_extern.h> 52e4eb384bSBosko Milekic #include <vm/memguard.h> 53e4eb384bSBosko Milekic 54e4eb384bSBosko Milekic /* 55e4eb384bSBosko Milekic * Global MemGuard data. 56e4eb384bSBosko Milekic */ 57e4eb384bSBosko Milekic static vm_map_t memguard_map; 58e4eb384bSBosko Milekic static unsigned long memguard_mapsize; 59e4eb384bSBosko Milekic static unsigned long memguard_mapused; 60e4eb384bSBosko Milekic struct memguard_entry { 61e4eb384bSBosko Milekic STAILQ_ENTRY(memguard_entry) entries; 62e4eb384bSBosko Milekic void *ptr; 63e4eb384bSBosko Milekic }; 64e4eb384bSBosko Milekic static STAILQ_HEAD(memguard_fifo, memguard_entry) memguard_fifo_pool; 65e4eb384bSBosko Milekic 66e4eb384bSBosko Milekic /* 67e4eb384bSBosko Milekic * Local prototypes. 68e4eb384bSBosko Milekic */ 69e4eb384bSBosko Milekic static void memguard_guard(void *addr); 70e4eb384bSBosko Milekic static void memguard_unguard(void *addr); 71e4eb384bSBosko Milekic 72e4eb384bSBosko Milekic /* 73e4eb384bSBosko Milekic * Local macros. MemGuard data is global, so replace these with whatever 74e4eb384bSBosko Milekic * your system uses to protect global data (if it is kernel-level 75e4eb384bSBosko Milekic * parallelized). This is for porting among BSDs. 76e4eb384bSBosko Milekic */ 77e4eb384bSBosko Milekic #define MEMGUARD_CRIT_SECTION_DECLARE static struct mtx memguard_mtx 78e4eb384bSBosko Milekic #define MEMGUARD_CRIT_SECTION_INIT \ 79e4eb384bSBosko Milekic mtx_init(&memguard_mtx, "MemGuard mtx", NULL, MTX_DEF) 80e4eb384bSBosko Milekic #define MEMGUARD_CRIT_SECTION_ENTER mtx_lock(&memguard_mtx) 81e4eb384bSBosko Milekic #define MEMGUARD_CRIT_SECTION_EXIT mtx_unlock(&memguard_mtx) 82e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_DECLARE; 83e4eb384bSBosko Milekic 84e4eb384bSBosko Milekic /* 85e4eb384bSBosko Milekic * Initialize the MemGuard mock allocator. All objects from MemGuard come 86e4eb384bSBosko Milekic * out of a single VM map (contiguous chunk of address space). 87e4eb384bSBosko Milekic */ 88e4eb384bSBosko Milekic void 89e4eb384bSBosko Milekic memguard_init(vm_map_t parent_map, unsigned long size) 90e4eb384bSBosko Milekic { 91e4eb384bSBosko Milekic char *base, *limit; 92e4eb384bSBosko Milekic 93e4eb384bSBosko Milekic /* size must be multiple of PAGE_SIZE */ 94e4eb384bSBosko Milekic size /= PAGE_SIZE; 95e4eb384bSBosko Milekic size++; 96e4eb384bSBosko Milekic size *= PAGE_SIZE; 97e4eb384bSBosko Milekic 98e4eb384bSBosko Milekic memguard_map = kmem_suballoc(parent_map, (vm_offset_t *)&base, 99e4eb384bSBosko Milekic (vm_offset_t *)&limit, (vm_size_t)size); 100e4eb384bSBosko Milekic memguard_map->system_map = 1; 101e4eb384bSBosko Milekic memguard_mapsize = size; 102e4eb384bSBosko Milekic memguard_mapused = 0; 103e4eb384bSBosko Milekic 104e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_INIT; 105e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_ENTER; 106e4eb384bSBosko Milekic STAILQ_INIT(&memguard_fifo_pool); 107e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_EXIT; 108e4eb384bSBosko Milekic 109e4eb384bSBosko Milekic printf("MEMGUARD DEBUGGING ALLOCATOR INITIALIZED:\n"); 110e4eb384bSBosko Milekic printf("\tMEMGUARD map base: %p\n", base); 111e4eb384bSBosko Milekic printf("\tMEMGUARD map limit: %p\n", limit); 112e4eb384bSBosko Milekic printf("\tMEMGUARD map size: %ld (Bytes)\n", size); 113e4eb384bSBosko Milekic } 114e4eb384bSBosko Milekic 115e4eb384bSBosko Milekic /* 116e4eb384bSBosko Milekic * Allocate a single object of specified size with specified flags (either 117e4eb384bSBosko Milekic * M_WAITOK or M_NOWAIT). 118e4eb384bSBosko Milekic */ 119e4eb384bSBosko Milekic void * 120e4eb384bSBosko Milekic memguard_alloc(unsigned long size, int flags) 121e4eb384bSBosko Milekic { 122e4eb384bSBosko Milekic void *obj = NULL; 123e4eb384bSBosko Milekic struct memguard_entry *e = NULL; 124e4eb384bSBosko Milekic 125e4eb384bSBosko Milekic /* XXX: MemGuard does not handle > PAGE_SIZE objects. */ 126e4eb384bSBosko Milekic if (size > PAGE_SIZE) 127e4eb384bSBosko Milekic panic("MEMGUARD: Cannot handle objects > PAGE_SIZE"); 128e4eb384bSBosko Milekic 129e4eb384bSBosko Milekic /* 130e4eb384bSBosko Milekic * If we haven't exhausted the memguard_map yet, allocate from 131e4eb384bSBosko Milekic * it and grab a new page, even if we have recycled pages in our 132e4eb384bSBosko Milekic * FIFO. This is because we wish to allow recycled pages to live 133e4eb384bSBosko Milekic * guarded in the FIFO for as long as possible in order to catch 134e4eb384bSBosko Milekic * even very late tamper-after-frees, even though it means that 135e4eb384bSBosko Milekic * we end up wasting more memory, this is only a DEBUGGING allocator 136e4eb384bSBosko Milekic * after all. 137e4eb384bSBosko Milekic */ 138e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_ENTER; 139e4eb384bSBosko Milekic if (memguard_mapused >= memguard_mapsize) { 140e4eb384bSBosko Milekic e = STAILQ_FIRST(&memguard_fifo_pool); 141e4eb384bSBosko Milekic if (e != NULL) { 142e4eb384bSBosko Milekic STAILQ_REMOVE(&memguard_fifo_pool, e, 143e4eb384bSBosko Milekic memguard_entry, entries); 144e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_EXIT; 145e4eb384bSBosko Milekic obj = e->ptr; 146e4eb384bSBosko Milekic free(e, M_TEMP); 147e4eb384bSBosko Milekic memguard_unguard(obj); 148e4eb384bSBosko Milekic if (flags & M_ZERO) 149e4eb384bSBosko Milekic bzero(obj, PAGE_SIZE); 150e4eb384bSBosko Milekic return obj; 151e4eb384bSBosko Milekic } 152e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_EXIT; 153e4eb384bSBosko Milekic if (flags & M_WAITOK) 154e4eb384bSBosko Milekic panic("MEMGUARD: Failed with M_WAITOK: " \ 155e4eb384bSBosko Milekic "memguard_map too small"); 156e4eb384bSBosko Milekic return NULL; 157e4eb384bSBosko Milekic } else 158e4eb384bSBosko Milekic memguard_mapused += PAGE_SIZE; 159e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_EXIT; 160e4eb384bSBosko Milekic 161e4eb384bSBosko Milekic if (obj == NULL) 162e4eb384bSBosko Milekic obj = (void *)kmem_malloc(memguard_map, PAGE_SIZE, flags); 163e4eb384bSBosko Milekic if (obj != NULL) { 164e4eb384bSBosko Milekic memguard_unguard(obj); 165e4eb384bSBosko Milekic if (flags & M_ZERO) 166e4eb384bSBosko Milekic bzero(obj, PAGE_SIZE); 167e4eb384bSBosko Milekic } else { 168e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_ENTER; 169e4eb384bSBosko Milekic memguard_mapused -= PAGE_SIZE; 170e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_EXIT; 171e4eb384bSBosko Milekic } 172e4eb384bSBosko Milekic return obj; 173e4eb384bSBosko Milekic } 174e4eb384bSBosko Milekic 175e4eb384bSBosko Milekic /* 176e4eb384bSBosko Milekic * Free specified single object. 177e4eb384bSBosko Milekic */ 178e4eb384bSBosko Milekic void 179e4eb384bSBosko Milekic memguard_free(void *addr) 180e4eb384bSBosko Milekic { 181e4eb384bSBosko Milekic struct memguard_entry *e; 182e4eb384bSBosko Milekic 183e4eb384bSBosko Milekic memguard_guard(addr); 184e4eb384bSBosko Milekic e = malloc(sizeof(struct memguard_entry), M_TEMP, M_NOWAIT); 185e4eb384bSBosko Milekic if (e == NULL) { 186e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_ENTER; 187e4eb384bSBosko Milekic memguard_mapused -= PAGE_SIZE; 188e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_EXIT; 189eca64e79SBosko Milekic kmem_free(memguard_map, (vm_offset_t)trunc_page( 190e4eb384bSBosko Milekic (unsigned long)addr), PAGE_SIZE); 191e4eb384bSBosko Milekic return; 192e4eb384bSBosko Milekic } 193eca64e79SBosko Milekic e->ptr = (void *)trunc_page((unsigned long)addr); 194e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_ENTER; 195e4eb384bSBosko Milekic STAILQ_INSERT_TAIL(&memguard_fifo_pool, e, entries); 196e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_EXIT; 197e4eb384bSBosko Milekic } 198e4eb384bSBosko Milekic 199e4eb384bSBosko Milekic /* 200e4eb384bSBosko Milekic * Guard a page containing specified object (make it read-only so that 201e4eb384bSBosko Milekic * future writes to it fail). 202e4eb384bSBosko Milekic */ 203e4eb384bSBosko Milekic static void 204e4eb384bSBosko Milekic memguard_guard(void *addr) 205e4eb384bSBosko Milekic { 206eca64e79SBosko Milekic void *a = (void *)trunc_page((unsigned long)addr); 207e4eb384bSBosko Milekic (void)vm_map_protect(memguard_map, (vm_offset_t)a, 208e4eb384bSBosko Milekic (vm_offset_t)((unsigned long)a + PAGE_SIZE), VM_PROT_READ, 0); 209e4eb384bSBosko Milekic } 210e4eb384bSBosko Milekic 211e4eb384bSBosko Milekic /* 212e4eb384bSBosko Milekic * Unguard a page containing specified object (make it read-and-write to 213e4eb384bSBosko Milekic * allow full data access). 214e4eb384bSBosko Milekic */ 215e4eb384bSBosko Milekic static void 216e4eb384bSBosko Milekic memguard_unguard(void *addr) 217e4eb384bSBosko Milekic { 218eca64e79SBosko Milekic void *a = (void *)trunc_page((unsigned long)addr); 219e4eb384bSBosko Milekic (void)vm_map_protect(memguard_map, (vm_offset_t)a, 220e4eb384bSBosko Milekic (vm_offset_t)((unsigned long)a + PAGE_SIZE), 221e4eb384bSBosko Milekic VM_PROT_READ | VM_PROT_WRITE, 0); 222e4eb384bSBosko Milekic } 223