1e4eb384bSBosko Milekic /* 2e4eb384bSBosko Milekic * Copyright (c) 2005, 38076cb52SBosko Milekic * Bosko Milekic <bmilekic@FreeBSD.org>. All rights reserved. 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> 4903412565SBosko Milekic #include <vm/vm_param.h> 50e4eb384bSBosko Milekic #include <vm/vm_page.h> 51e4eb384bSBosko Milekic #include <vm/vm_map.h> 52e4eb384bSBosko Milekic #include <vm/vm_extern.h> 53e4eb384bSBosko Milekic #include <vm/memguard.h> 54e4eb384bSBosko Milekic 55e4eb384bSBosko Milekic /* 5603412565SBosko Milekic * The maximum number of pages allowed per allocation. If you're using 5703412565SBosko Milekic * MemGuard to override very large items (> MAX_PAGES_PER_ITEM in size), 5803412565SBosko Milekic * you need to increase MAX_PAGES_PER_ITEM. 5903412565SBosko Milekic */ 6003412565SBosko Milekic #define MAX_PAGES_PER_ITEM 64 6103412565SBosko Milekic 6203412565SBosko Milekic /* 63e4eb384bSBosko Milekic * Global MemGuard data. 64e4eb384bSBosko Milekic */ 65e4eb384bSBosko Milekic static vm_map_t memguard_map; 66e4eb384bSBosko Milekic static unsigned long memguard_mapsize; 67e4eb384bSBosko Milekic static unsigned long memguard_mapused; 68e4eb384bSBosko Milekic struct memguard_entry { 69e4eb384bSBosko Milekic STAILQ_ENTRY(memguard_entry) entries; 70e4eb384bSBosko Milekic void *ptr; 71e4eb384bSBosko Milekic }; 7203412565SBosko Milekic static struct memguard_fifo { 7303412565SBosko Milekic struct memguard_entry *stqh_first; 7403412565SBosko Milekic struct memguard_entry **stqh_last; 7503412565SBosko Milekic int index; 7603412565SBosko Milekic } memguard_fifo_pool[MAX_PAGES_PER_ITEM]; 77e4eb384bSBosko Milekic 78e4eb384bSBosko Milekic /* 79e4eb384bSBosko Milekic * Local prototypes. 80e4eb384bSBosko Milekic */ 8103412565SBosko Milekic static void memguard_guard(void *addr, int numpgs); 8203412565SBosko Milekic static void memguard_unguard(void *addr, int numpgs); 8303412565SBosko Milekic static struct memguard_fifo *vtomgfifo(vm_offset_t va); 8403412565SBosko Milekic static void vsetmgfifo(vm_offset_t va, struct memguard_fifo *mgfifo); 8503412565SBosko Milekic static void vclrmgfifo(vm_offset_t va); 86e4eb384bSBosko Milekic 87e4eb384bSBosko Milekic /* 88e4eb384bSBosko Milekic * Local macros. MemGuard data is global, so replace these with whatever 89e4eb384bSBosko Milekic * your system uses to protect global data (if it is kernel-level 90e4eb384bSBosko Milekic * parallelized). This is for porting among BSDs. 91e4eb384bSBosko Milekic */ 92e4eb384bSBosko Milekic #define MEMGUARD_CRIT_SECTION_DECLARE static struct mtx memguard_mtx 93e4eb384bSBosko Milekic #define MEMGUARD_CRIT_SECTION_INIT \ 94e4eb384bSBosko Milekic mtx_init(&memguard_mtx, "MemGuard mtx", NULL, MTX_DEF) 95e4eb384bSBosko Milekic #define MEMGUARD_CRIT_SECTION_ENTER mtx_lock(&memguard_mtx) 96e4eb384bSBosko Milekic #define MEMGUARD_CRIT_SECTION_EXIT mtx_unlock(&memguard_mtx) 97e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_DECLARE; 98e4eb384bSBosko Milekic 99e4eb384bSBosko Milekic /* 100e4eb384bSBosko Milekic * Initialize the MemGuard mock allocator. All objects from MemGuard come 101e4eb384bSBosko Milekic * out of a single VM map (contiguous chunk of address space). 102e4eb384bSBosko Milekic */ 103e4eb384bSBosko Milekic void 104e4eb384bSBosko Milekic memguard_init(vm_map_t parent_map, unsigned long size) 105e4eb384bSBosko Milekic { 106e4eb384bSBosko Milekic char *base, *limit; 10703412565SBosko Milekic int i; 108e4eb384bSBosko Milekic 109e4eb384bSBosko Milekic /* size must be multiple of PAGE_SIZE */ 110e4eb384bSBosko Milekic size /= PAGE_SIZE; 111e4eb384bSBosko Milekic size++; 112e4eb384bSBosko Milekic size *= PAGE_SIZE; 113e4eb384bSBosko Milekic 114e4eb384bSBosko Milekic memguard_map = kmem_suballoc(parent_map, (vm_offset_t *)&base, 115e4eb384bSBosko Milekic (vm_offset_t *)&limit, (vm_size_t)size); 116e4eb384bSBosko Milekic memguard_map->system_map = 1; 117e4eb384bSBosko Milekic memguard_mapsize = size; 118e4eb384bSBosko Milekic memguard_mapused = 0; 119e4eb384bSBosko Milekic 120e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_INIT; 121e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_ENTER; 12203412565SBosko Milekic for (i = 0; i < MAX_PAGES_PER_ITEM; i++) { 12303412565SBosko Milekic STAILQ_INIT(&memguard_fifo_pool[i]); 12403412565SBosko Milekic memguard_fifo_pool[i].index = i; 12503412565SBosko Milekic } 126e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_EXIT; 127e4eb384bSBosko Milekic 128e4eb384bSBosko Milekic printf("MEMGUARD DEBUGGING ALLOCATOR INITIALIZED:\n"); 129e4eb384bSBosko Milekic printf("\tMEMGUARD map base: %p\n", base); 130e4eb384bSBosko Milekic printf("\tMEMGUARD map limit: %p\n", limit); 131e4eb384bSBosko Milekic printf("\tMEMGUARD map size: %ld (Bytes)\n", size); 132e4eb384bSBosko Milekic } 133e4eb384bSBosko Milekic 134e4eb384bSBosko Milekic /* 135e4eb384bSBosko Milekic * Allocate a single object of specified size with specified flags (either 136e4eb384bSBosko Milekic * M_WAITOK or M_NOWAIT). 137e4eb384bSBosko Milekic */ 138e4eb384bSBosko Milekic void * 139e4eb384bSBosko Milekic memguard_alloc(unsigned long size, int flags) 140e4eb384bSBosko Milekic { 14103412565SBosko Milekic void *obj; 142e4eb384bSBosko Milekic struct memguard_entry *e = NULL; 14303412565SBosko Milekic int numpgs; 144e4eb384bSBosko Milekic 14503412565SBosko Milekic numpgs = size / PAGE_SIZE; 14603412565SBosko Milekic if ((size % PAGE_SIZE) != 0) 14703412565SBosko Milekic numpgs++; 14803412565SBosko Milekic if (numpgs > MAX_PAGES_PER_ITEM) 14903412565SBosko Milekic panic("MEMGUARD: You must increase MAX_PAGES_PER_ITEM " \ 15003412565SBosko Milekic "in memguard.c (requested: %d pages)", numpgs); 15103412565SBosko Milekic if (numpgs == 0) 15203412565SBosko Milekic return NULL; 153e4eb384bSBosko Milekic 154e4eb384bSBosko Milekic /* 155e4eb384bSBosko Milekic * If we haven't exhausted the memguard_map yet, allocate from 156e4eb384bSBosko Milekic * it and grab a new page, even if we have recycled pages in our 157e4eb384bSBosko Milekic * FIFO. This is because we wish to allow recycled pages to live 158e4eb384bSBosko Milekic * guarded in the FIFO for as long as possible in order to catch 159e4eb384bSBosko Milekic * even very late tamper-after-frees, even though it means that 160e4eb384bSBosko Milekic * we end up wasting more memory, this is only a DEBUGGING allocator 161e4eb384bSBosko Milekic * after all. 162e4eb384bSBosko Milekic */ 163e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_ENTER; 164e4eb384bSBosko Milekic if (memguard_mapused >= memguard_mapsize) { 16503412565SBosko Milekic e = STAILQ_FIRST(&memguard_fifo_pool[numpgs - 1]); 166e4eb384bSBosko Milekic if (e != NULL) { 16703412565SBosko Milekic STAILQ_REMOVE(&memguard_fifo_pool[numpgs - 1], e, 168e4eb384bSBosko Milekic memguard_entry, entries); 169e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_EXIT; 170e4eb384bSBosko Milekic obj = e->ptr; 171e4eb384bSBosko Milekic free(e, M_TEMP); 17203412565SBosko Milekic memguard_unguard(obj, numpgs); 173e4eb384bSBosko Milekic if (flags & M_ZERO) 17403412565SBosko Milekic bzero(obj, PAGE_SIZE * numpgs); 175e4eb384bSBosko Milekic return obj; 176e4eb384bSBosko Milekic } 177e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_EXIT; 178e4eb384bSBosko Milekic if (flags & M_WAITOK) 179e4eb384bSBosko Milekic panic("MEMGUARD: Failed with M_WAITOK: " \ 180e4eb384bSBosko Milekic "memguard_map too small"); 181e4eb384bSBosko Milekic return NULL; 18203412565SBosko Milekic } 18303412565SBosko Milekic memguard_mapused += (PAGE_SIZE * numpgs); 184e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_EXIT; 185e4eb384bSBosko Milekic 18603412565SBosko Milekic obj = (void *)kmem_malloc(memguard_map, PAGE_SIZE * numpgs, flags); 187e4eb384bSBosko Milekic if (obj != NULL) { 18803412565SBosko Milekic vsetmgfifo((vm_offset_t)obj, &memguard_fifo_pool[numpgs - 1]); 189e4eb384bSBosko Milekic if (flags & M_ZERO) 19003412565SBosko Milekic bzero(obj, PAGE_SIZE * numpgs); 191e4eb384bSBosko Milekic } else { 192e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_ENTER; 19303412565SBosko Milekic memguard_mapused -= (PAGE_SIZE * numpgs); 194e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_EXIT; 195e4eb384bSBosko Milekic } 196e4eb384bSBosko Milekic return obj; 197e4eb384bSBosko Milekic } 198e4eb384bSBosko Milekic 199e4eb384bSBosko Milekic /* 200e4eb384bSBosko Milekic * Free specified single object. 201e4eb384bSBosko Milekic */ 202e4eb384bSBosko Milekic void 203e4eb384bSBosko Milekic memguard_free(void *addr) 204e4eb384bSBosko Milekic { 205e4eb384bSBosko Milekic struct memguard_entry *e; 20603412565SBosko Milekic struct memguard_fifo *mgfifo; 20703412565SBosko Milekic int idx; 20803412565SBosko Milekic int *temp; 209e4eb384bSBosko Milekic 21003412565SBosko Milekic addr = (void *)trunc_page((unsigned long)addr); 21103412565SBosko Milekic 21203412565SBosko Milekic /* 21303412565SBosko Milekic * Page should not be guarded by now, so force a write. 21403412565SBosko Milekic * The purpose of this is to increase the likelihood of catching a 21503412565SBosko Milekic * double-free, but not necessarily a tamper-after-free (the second 21603412565SBosko Milekic * thread freeing might not write before freeing, so this forces it 21703412565SBosko Milekic * to and, subsequently, trigger a fault). 21803412565SBosko Milekic */ 21903412565SBosko Milekic temp = (int *)((unsigned long)addr + (PAGE_SIZE/2)); /* in page */ 22003412565SBosko Milekic *temp = 0xd34dc0d3; 22103412565SBosko Milekic 22203412565SBosko Milekic mgfifo = vtomgfifo((vm_offset_t)addr); 22303412565SBosko Milekic idx = mgfifo->index; 22403412565SBosko Milekic memguard_guard(addr, idx + 1); 225e4eb384bSBosko Milekic e = malloc(sizeof(struct memguard_entry), M_TEMP, M_NOWAIT); 226e4eb384bSBosko Milekic if (e == NULL) { 227e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_ENTER; 22803412565SBosko Milekic memguard_mapused -= (PAGE_SIZE * (idx + 1)); 229e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_EXIT; 23003412565SBosko Milekic memguard_unguard(addr, idx + 1); /* just in case */ 23103412565SBosko Milekic vclrmgfifo((vm_offset_t)addr); 23203412565SBosko Milekic kmem_free(memguard_map, (vm_offset_t)addr, 23303412565SBosko Milekic PAGE_SIZE * (idx + 1)); 234e4eb384bSBosko Milekic return; 235e4eb384bSBosko Milekic } 23603412565SBosko Milekic e->ptr = addr; 237e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_ENTER; 23803412565SBosko Milekic STAILQ_INSERT_TAIL(mgfifo, e, entries); 239e4eb384bSBosko Milekic MEMGUARD_CRIT_SECTION_EXIT; 240e4eb384bSBosko Milekic } 241e4eb384bSBosko Milekic 242e4eb384bSBosko Milekic /* 243e4eb384bSBosko Milekic * Guard a page containing specified object (make it read-only so that 244e4eb384bSBosko Milekic * future writes to it fail). 245e4eb384bSBosko Milekic */ 246e4eb384bSBosko Milekic static void 24703412565SBosko Milekic memguard_guard(void *addr, int numpgs) 248e4eb384bSBosko Milekic { 249eca64e79SBosko Milekic void *a = (void *)trunc_page((unsigned long)addr); 25003412565SBosko Milekic if (vm_map_protect(memguard_map, (vm_offset_t)a, 25103412565SBosko Milekic (vm_offset_t)((unsigned long)a + (PAGE_SIZE * numpgs)), 25203412565SBosko Milekic VM_PROT_READ, FALSE) != KERN_SUCCESS) 25303412565SBosko Milekic panic("MEMGUARD: Unable to guard page!"); 254e4eb384bSBosko Milekic } 255e4eb384bSBosko Milekic 256e4eb384bSBosko Milekic /* 257e4eb384bSBosko Milekic * Unguard a page containing specified object (make it read-and-write to 258e4eb384bSBosko Milekic * allow full data access). 259e4eb384bSBosko Milekic */ 260e4eb384bSBosko Milekic static void 26103412565SBosko Milekic memguard_unguard(void *addr, int numpgs) 262e4eb384bSBosko Milekic { 263eca64e79SBosko Milekic void *a = (void *)trunc_page((unsigned long)addr); 26403412565SBosko Milekic if (vm_map_protect(memguard_map, (vm_offset_t)a, 26503412565SBosko Milekic (vm_offset_t)((unsigned long)a + (PAGE_SIZE * numpgs)), 26603412565SBosko Milekic VM_PROT_DEFAULT, FALSE) != KERN_SUCCESS) 26703412565SBosko Milekic panic("MEMGUARD: Unable to unguard page!"); 26803412565SBosko Milekic } 26903412565SBosko Milekic 27003412565SBosko Milekic /* 27103412565SBosko Milekic * vtomgfifo() converts a virtual address of the first page allocated for 27203412565SBosko Milekic * an item to a memguard_fifo_pool reference for the corresponding item's 27303412565SBosko Milekic * size. 27403412565SBosko Milekic * 27503412565SBosko Milekic * vsetmgfifo() sets a reference in an underlying page for the specified 27603412565SBosko Milekic * virtual address to an appropriate memguard_fifo_pool. 27703412565SBosko Milekic * 2787fae6a11SBosko Milekic * These routines are very similar to those defined by UMA in uma_int.h. 2797fae6a11SBosko Milekic * The difference is that these routines store the mgfifo in one of the 2807fae6a11SBosko Milekic * page's fields that is unused when the page is wired rather than the 2817fae6a11SBosko Milekic * object field, which is used. 28203412565SBosko Milekic */ 28303412565SBosko Milekic static struct memguard_fifo * 28403412565SBosko Milekic vtomgfifo(vm_offset_t va) 28503412565SBosko Milekic { 28603412565SBosko Milekic vm_page_t p; 28703412565SBosko Milekic struct memguard_fifo *mgfifo; 28803412565SBosko Milekic 28903412565SBosko Milekic p = PHYS_TO_VM_PAGE(pmap_kextract(va)); 2907fae6a11SBosko Milekic KASSERT(p->wire_count != 0 && p->queue == PQ_NONE, 2917fae6a11SBosko Milekic ("MEMGUARD: Expected wired page in vtomgfifo!")); 2927fae6a11SBosko Milekic mgfifo = (struct memguard_fifo *)p->pageq.tqe_next; 29303412565SBosko Milekic return mgfifo; 29403412565SBosko Milekic } 29503412565SBosko Milekic 29603412565SBosko Milekic static void 29703412565SBosko Milekic vsetmgfifo(vm_offset_t va, struct memguard_fifo *mgfifo) 29803412565SBosko Milekic { 29903412565SBosko Milekic vm_page_t p; 30003412565SBosko Milekic 30103412565SBosko Milekic p = PHYS_TO_VM_PAGE(pmap_kextract(va)); 3027fae6a11SBosko Milekic KASSERT(p->wire_count != 0 && p->queue == PQ_NONE, 3037fae6a11SBosko Milekic ("MEMGUARD: Expected wired page in vsetmgfifo!")); 3047fae6a11SBosko Milekic p->pageq.tqe_next = (vm_page_t)mgfifo; 30503412565SBosko Milekic } 30603412565SBosko Milekic 30703412565SBosko Milekic static void vclrmgfifo(vm_offset_t va) 30803412565SBosko Milekic { 30903412565SBosko Milekic vm_page_t p; 31003412565SBosko Milekic 31103412565SBosko Milekic p = PHYS_TO_VM_PAGE(pmap_kextract(va)); 3127fae6a11SBosko Milekic KASSERT(p->wire_count != 0 && p->queue == PQ_NONE, 3137fae6a11SBosko Milekic ("MEMGUARD: Expected wired page in vclrmgfifo!")); 3147fae6a11SBosko Milekic p->pageq.tqe_next = NULL; 315e4eb384bSBosko Milekic } 316