3 * Bosko Milekic <bmilekic@FreeBSD.org>. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice unmodified, this list of conditions, and the following
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
31 * MemGuard is a simple replacement allocator for debugging only
32 * which provides ElectricFence-style memory barrier protection on
33 * objects being allocated, and is used to detect tampering-after-free
36 * See the memguard(9) man page for more information on using MemGuard.
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/types.h>
43 #include <sys/queue.h>
45 #include <sys/mutex.h>
46 #include <sys/malloc.h>
49 #include <vm/vm_param.h>
50 #include <vm/vm_page.h>
51 #include <vm/vm_map.h>
52 #include <vm/vm_extern.h>
53 #include <vm/memguard.h>
56 * The maximum number of pages allowed per allocation. If you're using
57 * MemGuard to override very large items (> MAX_PAGES_PER_ITEM in size),
58 * you need to increase MAX_PAGES_PER_ITEM.
60 #define MAX_PAGES_PER_ITEM 64
63 * Global MemGuard data.
65 static vm_map_t memguard_map;
66 static unsigned long memguard_mapsize;
67 static unsigned long memguard_mapused;
68 struct memguard_entry {
69 STAILQ_ENTRY(memguard_entry) entries;
72 static struct memguard_fifo {
73 struct memguard_entry *stqh_first;
74 struct memguard_entry **stqh_last;
76 } memguard_fifo_pool[MAX_PAGES_PER_ITEM];
81 static void memguard_guard(void *addr, int numpgs);
82 static void memguard_unguard(void *addr, int numpgs);
83 static struct memguard_fifo *vtomgfifo(vm_offset_t va);
84 static void vsetmgfifo(vm_offset_t va, struct memguard_fifo *mgfifo);
85 static void vclrmgfifo(vm_offset_t va);
88 * Local macros. MemGuard data is global, so replace these with whatever
89 * your system uses to protect global data (if it is kernel-level
90 * parallelized). This is for porting among BSDs.
92 #define MEMGUARD_CRIT_SECTION_DECLARE static struct mtx memguard_mtx
93 #define MEMGUARD_CRIT_SECTION_INIT \
94 mtx_init(&memguard_mtx, "MemGuard mtx", NULL, MTX_DEF)
95 #define MEMGUARD_CRIT_SECTION_ENTER mtx_lock(&memguard_mtx)
96 #define MEMGUARD_CRIT_SECTION_EXIT mtx_unlock(&memguard_mtx)
97 MEMGUARD_CRIT_SECTION_DECLARE;
100 * Initialize the MemGuard mock allocator. All objects from MemGuard come
101 * out of a single VM map (contiguous chunk of address space).
104 memguard_init(vm_map_t parent_map, unsigned long size)
109 /* size must be multiple of PAGE_SIZE */
114 memguard_map = kmem_suballoc(parent_map, (vm_offset_t *)&base,
115 (vm_offset_t *)&limit, (vm_size_t)size);
116 memguard_map->system_map = 1;
117 memguard_mapsize = size;
118 memguard_mapused = 0;
120 MEMGUARD_CRIT_SECTION_INIT;
121 MEMGUARD_CRIT_SECTION_ENTER;
122 for (i = 0; i < MAX_PAGES_PER_ITEM; i++) {
123 STAILQ_INIT(&memguard_fifo_pool[i]);
124 memguard_fifo_pool[i].index = i;
126 MEMGUARD_CRIT_SECTION_EXIT;
128 printf("MEMGUARD DEBUGGING ALLOCATOR INITIALIZED:\n");
129 printf("\tMEMGUARD map base: %p\n", base);
130 printf("\tMEMGUARD map limit: %p\n", limit);
131 printf("\tMEMGUARD map size: %ld (Bytes)\n", size);
135 * Allocate a single object of specified size with specified flags (either
136 * M_WAITOK or M_NOWAIT).
139 memguard_alloc(unsigned long size, int flags)
142 struct memguard_entry *e = NULL;
145 numpgs = size / PAGE_SIZE;
146 if ((size % PAGE_SIZE) != 0)
148 if (numpgs > MAX_PAGES_PER_ITEM)
149 panic("MEMGUARD: You must increase MAX_PAGES_PER_ITEM " \
150 "in memguard.c (requested: %d pages)", numpgs);
155 * If we haven't exhausted the memguard_map yet, allocate from
156 * it and grab a new page, even if we have recycled pages in our
157 * FIFO. This is because we wish to allow recycled pages to live
158 * guarded in the FIFO for as long as possible in order to catch
159 * even very late tamper-after-frees, even though it means that
160 * we end up wasting more memory, this is only a DEBUGGING allocator
163 MEMGUARD_CRIT_SECTION_ENTER;
164 if (memguard_mapused >= memguard_mapsize) {
165 e = STAILQ_FIRST(&memguard_fifo_pool[numpgs - 1]);
167 STAILQ_REMOVE(&memguard_fifo_pool[numpgs - 1], e,
168 memguard_entry, entries);
169 MEMGUARD_CRIT_SECTION_EXIT;
172 memguard_unguard(obj, numpgs);
174 bzero(obj, PAGE_SIZE * numpgs);
177 MEMGUARD_CRIT_SECTION_EXIT;
178 if (flags & M_WAITOK)
179 panic("MEMGUARD: Failed with M_WAITOK: " \
180 "memguard_map too small");
183 memguard_mapused += (PAGE_SIZE * numpgs);
184 MEMGUARD_CRIT_SECTION_EXIT;
186 obj = (void *)kmem_malloc(memguard_map, PAGE_SIZE * numpgs, flags);
188 vsetmgfifo((vm_offset_t)obj, &memguard_fifo_pool[numpgs - 1]);
190 bzero(obj, PAGE_SIZE * numpgs);
192 MEMGUARD_CRIT_SECTION_ENTER;
193 memguard_mapused -= (PAGE_SIZE * numpgs);
194 MEMGUARD_CRIT_SECTION_EXIT;
200 * Free specified single object.
203 memguard_free(void *addr)
205 struct memguard_entry *e;
206 struct memguard_fifo *mgfifo;
210 addr = (void *)trunc_page((unsigned long)addr);
213 * Page should not be guarded by now, so force a write.
214 * The purpose of this is to increase the likelihood of catching a
215 * double-free, but not necessarily a tamper-after-free (the second
216 * thread freeing might not write before freeing, so this forces it
217 * to and, subsequently, trigger a fault).
219 temp = (int *)((unsigned long)addr + (PAGE_SIZE/2)); /* in page */
222 mgfifo = vtomgfifo((vm_offset_t)addr);
224 memguard_guard(addr, idx + 1);
225 e = malloc(sizeof(struct memguard_entry), M_TEMP, M_NOWAIT);
227 MEMGUARD_CRIT_SECTION_ENTER;
228 memguard_mapused -= (PAGE_SIZE * (idx + 1));
229 MEMGUARD_CRIT_SECTION_EXIT;
230 memguard_unguard(addr, idx + 1); /* just in case */
231 vclrmgfifo((vm_offset_t)addr);
232 kmem_free(memguard_map, (vm_offset_t)addr,
233 PAGE_SIZE * (idx + 1));
237 MEMGUARD_CRIT_SECTION_ENTER;
238 STAILQ_INSERT_TAIL(mgfifo, e, entries);
239 MEMGUARD_CRIT_SECTION_EXIT;
243 * Guard a page containing specified object (make it read-only so that
244 * future writes to it fail).
247 memguard_guard(void *addr, int numpgs)
249 void *a = (void *)trunc_page((unsigned long)addr);
250 if (vm_map_protect(memguard_map, (vm_offset_t)a,
251 (vm_offset_t)((unsigned long)a + (PAGE_SIZE * numpgs)),
252 VM_PROT_READ, FALSE) != KERN_SUCCESS)
253 panic("MEMGUARD: Unable to guard page!");
257 * Unguard a page containing specified object (make it read-and-write to
258 * allow full data access).
261 memguard_unguard(void *addr, int numpgs)
263 void *a = (void *)trunc_page((unsigned long)addr);
264 if (vm_map_protect(memguard_map, (vm_offset_t)a,
265 (vm_offset_t)((unsigned long)a + (PAGE_SIZE * numpgs)),
266 VM_PROT_DEFAULT, FALSE) != KERN_SUCCESS)
267 panic("MEMGUARD: Unable to unguard page!");
271 * vtomgfifo() converts a virtual address of the first page allocated for
272 * an item to a memguard_fifo_pool reference for the corresponding item's
275 * vsetmgfifo() sets a reference in an underlying page for the specified
276 * virtual address to an appropriate memguard_fifo_pool.
278 * These routines are very similar to those defined by UMA in uma_int.h.
279 * The difference is that these routines store the mgfifo in one of the
280 * page's fields that is unused when the page is wired rather than the
281 * object field, which is used.
283 static struct memguard_fifo *
284 vtomgfifo(vm_offset_t va)
287 struct memguard_fifo *mgfifo;
289 p = PHYS_TO_VM_PAGE(pmap_kextract(va));
290 KASSERT(p->wire_count != 0 && p->queue == PQ_NONE,
291 ("MEMGUARD: Expected wired page in vtomgfifo!"));
292 mgfifo = (struct memguard_fifo *)p->pageq.tqe_next;
297 vsetmgfifo(vm_offset_t va, struct memguard_fifo *mgfifo)
301 p = PHYS_TO_VM_PAGE(pmap_kextract(va));
302 KASSERT(p->wire_count != 0 && p->queue == PQ_NONE,
303 ("MEMGUARD: Expected wired page in vsetmgfifo!"));
304 p->pageq.tqe_next = (vm_page_t)mgfifo;
307 static void vclrmgfifo(vm_offset_t va)
311 p = PHYS_TO_VM_PAGE(pmap_kextract(va));
312 KASSERT(p->wire_count != 0 && p->queue == PQ_NONE,
313 ("MEMGUARD: Expected wired page in vclrmgfifo!"));
314 p->pageq.tqe_next = NULL;