xref: /haiku/src/system/kernel/guarded_heap.cpp (revision 83b732c7328f1c4601e1a64ce234b93615c0ddc8)
1 /*
2  * Copyright 2011, Michael Lotz <mmlr@mlotz.ch>.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <stdio.h>
8 #include <string.h>
9 
10 #include <arch/debug.h>
11 #include <elf.h>
12 #include <debug.h>
13 #include <heap.h>
14 #include <malloc.h>
15 #include <slab/Slab.h>
16 #include <tracing.h>
17 #include <util/list.h>
18 #include <util/AutoLock.h>
19 #include <vm/vm.h>
20 
21 
22 #if USE_GUARDED_HEAP_FOR_MALLOC
23 
24 
25 #define GUARDED_HEAP_PAGE_FLAG_USED		0x01
26 #define GUARDED_HEAP_PAGE_FLAG_FIRST	0x02
27 #define GUARDED_HEAP_PAGE_FLAG_GUARD	0x04
28 #define GUARDED_HEAP_PAGE_FLAG_DEAD		0x08
29 
30 #define GUARDED_HEAP_STACK_TRACE_DEPTH	0
31 
32 
33 struct guarded_heap;
34 
35 struct guarded_heap_page {
36 	uint8				flags;
37 	size_t				allocation_size;
38 	void*				allocation_base;
39 	size_t				alignment;
40 	thread_id			thread;
41 #if GUARDED_HEAP_STACK_TRACE_DEPTH > 0
42 	size_t				stack_trace_depth;
43 	addr_t				stack_trace[GUARDED_HEAP_STACK_TRACE_DEPTH];
44 #endif
45 	list_link			free_list_link;
46 };
47 
48 struct guarded_heap_area {
49 	guarded_heap*		heap;
50 	guarded_heap_area*	next;
51 	area_id				area;
52 	addr_t				base;
53 	size_t				size;
54 	size_t				page_count;
55 	size_t				used_pages;
56 	void*				protection_cookie;
57 	mutex				lock;
58 	struct list			free_list;
59 	guarded_heap_page	pages[0];
60 };
61 
62 struct guarded_heap {
63 	rw_lock				lock;
64 	size_t				page_count;
65 	size_t				used_pages;
66 	int32				area_creation_counter;
67 	guarded_heap_area*	areas;
68 };
69 
70 
71 static guarded_heap sGuardedHeap = {
72 	RW_LOCK_INITIALIZER("guarded heap lock"),
73 	0, 0, 0, NULL
74 };
75 
76 
77 #if GUARDED_HEAP_TRACING
78 
79 namespace GuardedHeapTracing {
80 
81 
82 class GuardedHeapTraceEntry
83 	: public TRACE_ENTRY_SELECTOR(GUARDED_HEAP_TRACING_STACK_TRACE) {
84 	public:
85 		GuardedHeapTraceEntry(guarded_heap* heap)
86 			:
87 			TraceEntryBase(GUARDED_HEAP_TRACING_STACK_TRACE, 0, true),
88 			fHeap(heap)
89 		{
90 		}
91 
92 	protected:
93 		guarded_heap*	fHeap;
94 };
95 
96 
97 class Allocate : public GuardedHeapTraceEntry {
98 	public:
99 		Allocate(guarded_heap* heap, void* pageBase, uint32 flags)
100 			:
101 			GuardedHeapTraceEntry(heap),
102 			fPageBase(pageBase),
103 			fFlags(flags)
104 		{
105 			Initialized();
106 		}
107 
108 		virtual void AddDump(TraceOutput& out)
109 		{
110 			out.Print("guarded heap allocate: heap: %p; page: %p; "
111 				"flags:%s%s%s%s", fHeap, fPageBase,
112 				(fFlags & GUARDED_HEAP_PAGE_FLAG_USED) != 0 ? " used" : "",
113 				(fFlags & GUARDED_HEAP_PAGE_FLAG_FIRST) != 0 ? " first" : "",
114 				(fFlags & GUARDED_HEAP_PAGE_FLAG_GUARD) != 0 ? " guard" : "",
115 				(fFlags & GUARDED_HEAP_PAGE_FLAG_DEAD) != 0 ? " dead" : "");
116 		}
117 
118 	private:
119 		void*		fPageBase;
120 		uint32		fFlags;
121 };
122 
123 
124 class Free : public GuardedHeapTraceEntry {
125 	public:
126 		Free(guarded_heap* heap, void* pageBase)
127 			:
128 			GuardedHeapTraceEntry(heap),
129 			fPageBase(pageBase)
130 		{
131 			Initialized();
132 		}
133 
134 		virtual void AddDump(TraceOutput& out)
135 		{
136 			out.Print("guarded heap free: heap: %p; page: %p", fHeap,
137 				fPageBase);
138 		}
139 
140 	private:
141 		void*		fPageBase;
142 };
143 
144 
145 }	// namespace GuardedHeapTracing
146 
147 #	define T(x)	new(std::nothrow) GuardedHeapTracing::x
148 #else
149 #	define T(x)
150 #endif	// GUARDED_HEAP_TRACING
151 
152 
153 static void
154 guarded_heap_page_protect(guarded_heap_area& area, size_t pageIndex,
155 	uint32 protection)
156 {
157 	if (area.area < 0)
158 		return;
159 
160 	addr_t address = area.base + pageIndex * B_PAGE_SIZE;
161 	vm_set_kernel_area_debug_protection(area.protection_cookie, (void*)address,
162 		B_PAGE_SIZE, protection);
163 }
164 
165 
166 static void
167 guarded_heap_page_allocate(guarded_heap_area& area, size_t startPageIndex,
168 	size_t pagesNeeded, size_t allocationSize, size_t alignment,
169 	void* allocationBase)
170 {
171 	if (pagesNeeded < 2) {
172 		panic("need to allocate at least 2 pages, one for guard\n");
173 		return;
174 	}
175 
176 	guarded_heap_page* firstPage = NULL;
177 	for (size_t i = 0; i < pagesNeeded; i++) {
178 		guarded_heap_page& page = area.pages[startPageIndex + i];
179 		page.flags = GUARDED_HEAP_PAGE_FLAG_USED;
180 		if (i == 0) {
181 			page.thread = find_thread(NULL);
182 #if GUARDED_HEAP_STACK_TRACE_DEPTH > 0
183 			page.stack_trace_depth = arch_debug_get_stack_trace(
184 				page.stack_trace, GUARDED_HEAP_STACK_TRACE_DEPTH, 0, 4,
185 				STACK_TRACE_KERNEL);
186 #endif
187 			page.allocation_size = allocationSize;
188 			page.allocation_base = allocationBase;
189 			page.alignment = alignment;
190 			page.flags |= GUARDED_HEAP_PAGE_FLAG_FIRST;
191 			firstPage = &page;
192 		} else {
193 			page.thread = firstPage->thread;
194 #if GUARDED_HEAP_STACK_TRACE_DEPTH > 0
195 			page.stack_trace_depth = 0;
196 #endif
197 			page.allocation_size = allocationSize;
198 			page.allocation_base = allocationBase;
199 			page.alignment = alignment;
200 		}
201 
202 		list_remove_item(&area.free_list, &page);
203 
204 		if (i == pagesNeeded - 1) {
205 			page.flags |= GUARDED_HEAP_PAGE_FLAG_GUARD;
206 			guarded_heap_page_protect(area, startPageIndex + i, 0);
207 		} else {
208 			guarded_heap_page_protect(area, startPageIndex + i,
209 				B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
210 		}
211 
212 		T(Allocate(area.heap,
213 			(void*)(area.base + (startPageIndex + i) * B_PAGE_SIZE),
214 			page.flags));
215 	}
216 }
217 
218 
219 static void
220 guarded_heap_free_page(guarded_heap_area& area, size_t pageIndex,
221 	bool force = false)
222 {
223 	guarded_heap_page& page = area.pages[pageIndex];
224 
225 #if DEBUG_GUARDED_HEAP_DISABLE_MEMORY_REUSE
226 	if (force || area.area < 0)
227 		page.flags = 0;
228 	else
229 		page.flags |= GUARDED_HEAP_PAGE_FLAG_DEAD;
230 #else
231 	page.flags = 0;
232 #endif
233 
234 	page.allocation_size = 0;
235 	page.thread = find_thread(NULL);
236 
237 #if GUARDED_HEAP_STACK_TRACE_DEPTH > 0
238 	page.stack_trace_depth = arch_debug_get_stack_trace(page.stack_trace,
239 		GUARDED_HEAP_STACK_TRACE_DEPTH, 0, 3, STACK_TRACE_KERNEL);
240 #endif
241 
242 	list_add_item(&area.free_list, &page);
243 
244 	guarded_heap_page_protect(area, pageIndex, 0);
245 
246 	T(Free(area.heap, (void*)(area.base + pageIndex * B_PAGE_SIZE)));
247 }
248 
249 
250 static bool
251 guarded_heap_pages_allocated(guarded_heap& heap, size_t pagesAllocated)
252 {
253 	return (atomic_add((int32*)&heap.used_pages, pagesAllocated)
254 			+ pagesAllocated)
255 		>= heap.page_count - HEAP_GROW_SIZE / B_PAGE_SIZE / 2;
256 }
257 
258 
259 static void*
260 guarded_heap_area_allocate(guarded_heap_area& area, size_t size,
261 	size_t alignment, uint32 flags, bool& grow)
262 {
263 	if (alignment > B_PAGE_SIZE) {
264 		panic("alignment of %" B_PRIuSIZE " not supported", alignment);
265 		return NULL;
266 	}
267 
268 	size_t pagesNeeded = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE + 1;
269 	if (pagesNeeded > area.page_count - area.used_pages)
270 		return NULL;
271 
272 	if (pagesNeeded > area.page_count)
273 		return NULL;
274 
275 	// We use the free list this way so that the page that has been free for
276 	// the longest time is allocated. This keeps immediate re-use (that may
277 	// hide bugs) to a minimum.
278 	guarded_heap_page* page
279 		= (guarded_heap_page*)list_get_first_item(&area.free_list);
280 
281 	for (; page != NULL;
282 		page = (guarded_heap_page*)list_get_next_item(&area.free_list, page)) {
283 
284 		if ((page->flags & GUARDED_HEAP_PAGE_FLAG_USED) != 0)
285 			continue;
286 
287 		size_t pageIndex = page - area.pages;
288 		if (pageIndex > area.page_count - pagesNeeded)
289 			continue;
290 
291 		// Candidate, check if we have enough pages going forward
292 		// (including the guard page).
293 		bool candidate = true;
294 		for (size_t j = 1; j < pagesNeeded; j++) {
295 			if ((area.pages[pageIndex + j].flags & GUARDED_HEAP_PAGE_FLAG_USED)
296 					!= 0) {
297 				candidate = false;
298 				break;
299 			}
300 		}
301 
302 		if (!candidate)
303 			continue;
304 
305 		if (alignment == 0)
306 			alignment = 1;
307 
308 		size_t offset = size & (B_PAGE_SIZE - 1);
309 		void* result = (void*)((area.base + pageIndex * B_PAGE_SIZE
310 			+ (offset > 0 ? B_PAGE_SIZE - offset : 0)) & ~(alignment - 1));
311 
312 		guarded_heap_page_allocate(area, pageIndex, pagesNeeded, size,
313 			alignment, result);
314 
315 		area.used_pages += pagesNeeded;
316 		grow = guarded_heap_pages_allocated(*area.heap, pagesNeeded);
317 		return result;
318 	}
319 
320 	return NULL;
321 }
322 
323 
324 static bool
325 guarded_heap_area_init(guarded_heap& heap, area_id id, void* baseAddress,
326 	size_t size, uint32 flags)
327 {
328 	guarded_heap_area* area = (guarded_heap_area*)baseAddress;
329 	area->heap = &heap;
330 	area->area = id;
331 	area->size = size;
332 	area->page_count = area->size / B_PAGE_SIZE;
333 	area->used_pages = 0;
334 
335 	size_t pagesNeeded = (sizeof(guarded_heap_area)
336 		+ area->page_count * sizeof(guarded_heap_page)
337 		+ B_PAGE_SIZE - 1) / B_PAGE_SIZE;
338 
339 	area->page_count -= pagesNeeded;
340 	area->size = area->page_count * B_PAGE_SIZE;
341 	area->base = (addr_t)baseAddress + pagesNeeded * B_PAGE_SIZE;
342 
343 	if (area->area >= 0 && vm_prepare_kernel_area_debug_protection(area->area,
344 			&area->protection_cookie) != B_OK) {
345 		return false;
346 	}
347 
348 	mutex_init(&area->lock, "guarded_heap_area_lock");
349 
350 	list_init_etc(&area->free_list,
351 		offsetof(guarded_heap_page, free_list_link));
352 
353 	for (size_t i = 0; i < area->page_count; i++)
354 		guarded_heap_free_page(*area, i, true);
355 
356 	WriteLocker areaListWriteLocker(heap.lock);
357 	area->next = heap.areas;
358 	heap.areas = area;
359 	heap.page_count += area->page_count;
360 
361 	return true;
362 }
363 
364 
365 static bool
366 guarded_heap_area_create(guarded_heap& heap, uint32 flags)
367 {
368 	for (size_t trySize = HEAP_GROW_SIZE; trySize >= 1 * 1024 * 1024;
369 		trySize /= 2) {
370 
371 		void* baseAddress = NULL;
372 		area_id id = create_area("guarded_heap_area", &baseAddress,
373 			B_ANY_KERNEL_ADDRESS, trySize, B_FULL_LOCK,
374 			B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
375 
376 		if (id < 0)
377 			continue;
378 
379 		if (guarded_heap_area_init(heap, id, baseAddress, trySize, flags))
380 			return true;
381 
382 		delete_area(id);
383 	}
384 
385 	panic("failed to allocate a new heap area");
386 	return false;
387 }
388 
389 
390 static bool
391 guarded_heap_add_area(guarded_heap& heap, int32 counter, uint32 flags)
392 {
393 	if ((flags & (HEAP_DONT_LOCK_KERNEL_SPACE | HEAP_DONT_WAIT_FOR_MEMORY))
394 			!= 0) {
395 		return false;
396 	}
397 
398 	if (atomic_test_and_set(&heap.area_creation_counter,
399 			counter + 1, counter) == counter) {
400 		return guarded_heap_area_create(heap, flags);
401 	}
402 
403 	return false;
404 }
405 
406 
407 static void*
408 guarded_heap_allocate(guarded_heap& heap, size_t size, size_t alignment,
409 	uint32 flags)
410 {
411 	bool grow = false;
412 	void* result = NULL;
413 	ReadLocker areaListReadLocker(heap.lock);
414 	for (guarded_heap_area* area = heap.areas; area != NULL;
415 			area = area->next) {
416 
417 		MutexLocker locker(area->lock);
418 		result = guarded_heap_area_allocate(*area, size, alignment, flags,
419 			grow);
420 		if (result != NULL)
421 			break;
422 	}
423 
424 	int32 counter = atomic_get(&heap.area_creation_counter);
425 	areaListReadLocker.Unlock();
426 
427 	if (result == NULL || grow) {
428 		bool added = guarded_heap_add_area(heap, counter, flags);
429 		if (result == NULL && added)
430 			return guarded_heap_allocate(heap, size, alignment, flags);
431 	}
432 
433 	if (result == NULL)
434 		panic("ran out of memory");
435 
436 	return result;
437 }
438 
439 
440 static guarded_heap_area*
441 guarded_heap_get_locked_area_for(guarded_heap& heap, void* address)
442 {
443 	ReadLocker areaListReadLocker(heap.lock);
444 	for (guarded_heap_area* area = heap.areas; area != NULL;
445 			area = area->next) {
446 		if ((addr_t)address < area->base)
447 			continue;
448 
449 		if ((addr_t)address >= area->base + area->size)
450 			continue;
451 
452 		mutex_lock(&area->lock);
453 		return area;
454 	}
455 
456 	panic("guarded heap area for address %p not found", address);
457 	return NULL;
458 }
459 
460 
461 static size_t
462 guarded_heap_area_page_index_for(guarded_heap_area& area, void* address)
463 {
464 	size_t pageIndex = ((addr_t)address - area.base) / B_PAGE_SIZE;
465 	guarded_heap_page& page = area.pages[pageIndex];
466 	if ((page.flags & GUARDED_HEAP_PAGE_FLAG_USED) == 0) {
467 		panic("tried to free %p which points at page %" B_PRIuSIZE
468 			" which is not marked in use", address, pageIndex);
469 		return area.page_count;
470 	}
471 
472 	if ((page.flags & GUARDED_HEAP_PAGE_FLAG_GUARD) != 0) {
473 		panic("tried to free %p which points at page %" B_PRIuSIZE
474 			" which is a guard page", address, pageIndex);
475 		return area.page_count;
476 	}
477 
478 	if ((page.flags & GUARDED_HEAP_PAGE_FLAG_FIRST) == 0) {
479 		panic("tried to free %p which points at page %" B_PRIuSIZE
480 			" which is not an allocation first page", address, pageIndex);
481 		return area.page_count;
482 	}
483 
484 	if ((page.flags & GUARDED_HEAP_PAGE_FLAG_DEAD) != 0) {
485 		panic("tried to free %p which points at page %" B_PRIuSIZE
486 			" which is a dead page", address, pageIndex);
487 		return area.page_count;
488 	}
489 
490 	return pageIndex;
491 }
492 
493 
494 static void
495 guarded_heap_area_free(guarded_heap_area& area, void* address, uint32 flags)
496 {
497 	size_t pageIndex = guarded_heap_area_page_index_for(area, address);
498 	if (pageIndex >= area.page_count)
499 		return;
500 
501 	size_t pagesFreed = 0;
502 	guarded_heap_page* page = &area.pages[pageIndex];
503 	while ((page->flags & GUARDED_HEAP_PAGE_FLAG_GUARD) == 0) {
504 		// Mark the allocation page as free.
505 		guarded_heap_free_page(area, pageIndex);
506 
507 		pagesFreed++;
508 		pageIndex++;
509 		page = &area.pages[pageIndex];
510 	}
511 
512 	// Mark the guard page as free as well.
513 	guarded_heap_free_page(area, pageIndex);
514 	pagesFreed++;
515 
516 #if !DEBUG_GUARDED_HEAP_DISABLE_MEMORY_REUSE
517 	area.used_pages -= pagesFreed;
518 	atomic_add((int32*)&area.heap->used_pages, -pagesFreed);
519 #endif
520 }
521 
522 
523 static void
524 guarded_heap_free(void* address, uint32 flags)
525 {
526 	if (address == NULL)
527 		return;
528 
529 	guarded_heap_area* area = guarded_heap_get_locked_area_for(sGuardedHeap,
530 		address);
531 	if (area == NULL)
532 		return;
533 
534 	MutexLocker locker(area->lock, true);
535 	guarded_heap_area_free(*area, address, flags);
536 }
537 
538 
539 static void*
540 guarded_heap_realloc(void* address, size_t newSize)
541 {
542 	guarded_heap_area* area = guarded_heap_get_locked_area_for(sGuardedHeap,
543 		address);
544 	if (area == NULL)
545 		return NULL;
546 
547 	MutexLocker locker(area->lock, true);
548 
549 	size_t pageIndex = guarded_heap_area_page_index_for(*area, address);
550 	if (pageIndex >= area->page_count)
551 		return NULL;
552 
553 	guarded_heap_page& page = area->pages[pageIndex];
554 	size_t oldSize = page.allocation_size;
555 	locker.Unlock();
556 
557 	if (oldSize == newSize)
558 		return address;
559 
560 	void* newBlock = memalign(0, newSize);
561 	if (newBlock == NULL)
562 		return NULL;
563 
564 	memcpy(newBlock, address, min_c(oldSize, newSize));
565 
566 	free(address);
567 
568 	return newBlock;
569 }
570 
571 
572 // #pragma mark - Debugger commands
573 
574 
575 static int
576 dump_guarded_heap_page(int argc, char** argv)
577 {
578 	if (argc != 2) {
579 		print_debugger_command_usage(argv[0]);
580 		return 0;
581 	}
582 
583 	addr_t address = parse_expression(argv[1]);
584 
585 	// Find the area that contains this page.
586 	guarded_heap_area* area = NULL;
587 	for (guarded_heap_area* candidate = sGuardedHeap.areas; candidate != NULL;
588 			candidate = candidate->next) {
589 
590 		if (address < candidate->base)
591 			continue;
592 		if (address >= candidate->base + candidate->size)
593 			continue;
594 
595 		area = candidate;
596 		break;
597 	}
598 
599 	if (area == NULL) {
600 		kprintf("didn't find area for address\n");
601 		return 1;
602 	}
603 
604 	size_t pageIndex = ((addr_t)address - area->base) / B_PAGE_SIZE;
605 	guarded_heap_page& page = area->pages[pageIndex];
606 
607 	kprintf("page index: %" B_PRIuSIZE "\n", pageIndex);
608 	kprintf("flags:");
609 	if ((page.flags & GUARDED_HEAP_PAGE_FLAG_USED) != 0)
610 		kprintf(" used");
611 	if ((page.flags & GUARDED_HEAP_PAGE_FLAG_FIRST) != 0)
612 		kprintf(" first");
613 	if ((page.flags & GUARDED_HEAP_PAGE_FLAG_GUARD) != 0)
614 		kprintf(" guard");
615 	if ((page.flags & GUARDED_HEAP_PAGE_FLAG_DEAD) != 0)
616 		kprintf(" dead");
617 	kprintf("\n");
618 
619 	kprintf("allocation size: %" B_PRIuSIZE "\n", page.allocation_size);
620 	kprintf("allocation base: %p\n", page.allocation_base);
621 	kprintf("alignment: %" B_PRIuSIZE "\n", page.alignment);
622 	kprintf("allocating thread: %" B_PRId32 "\n", page.thread);
623 
624 #if GUARDED_HEAP_STACK_TRACE_DEPTH > 0
625 	kprintf("stack trace:\n");
626 	for (size_t i = 0; i < page.stack_trace_depth; i++) {
627 		addr_t address = page.stack_trace[i];
628 
629 		const char* symbol;
630 		const char* imageName;
631 		bool exactMatch;
632 		addr_t baseAddress;
633 
634 		if (elf_debug_lookup_symbol_address(address, &baseAddress, &symbol,
635 				&imageName, &exactMatch) == B_OK) {
636 			kprintf("  %p  %s + 0x%lx (%s)%s\n", (void*)address, symbol,
637 				address - baseAddress, imageName,
638 				exactMatch ? "" : " (nearest)");
639 		} else
640 			kprintf("  %p\n", (void*)address);
641 	}
642 #endif
643 
644 	return 0;
645 }
646 
647 
648 static int
649 dump_guarded_heap_area(int argc, char** argv)
650 {
651 	if (argc != 2) {
652 		print_debugger_command_usage(argv[0]);
653 		return 0;
654 	}
655 
656 	addr_t address = parse_expression(argv[1]);
657 
658 	// Find the area that contains this page.
659 	guarded_heap_area* area = NULL;
660 	for (guarded_heap_area* candidate = sGuardedHeap.areas; candidate != NULL;
661 			candidate = candidate->next) {
662 
663 		if ((addr_t)candidate != address) {
664 			if (address < candidate->base)
665 				continue;
666 			if (address >= candidate->base + candidate->size)
667 				continue;
668 		}
669 
670 		area = candidate;
671 		break;
672 	}
673 
674 	if (area == NULL) {
675 		kprintf("didn't find area for address\n");
676 		return 1;
677 	}
678 
679 	kprintf("guarded heap area: %p\n", area);
680 	kprintf("next heap area: %p\n", area->next);
681 	kprintf("guarded heap: %p\n", area->heap);
682 	kprintf("area id: %" B_PRId32 "\n", area->area);
683 	kprintf("base: 0x%" B_PRIxADDR "\n", area->base);
684 	kprintf("size: %" B_PRIuSIZE "\n", area->size);
685 	kprintf("page count: %" B_PRIuSIZE "\n", area->page_count);
686 	kprintf("used pages: %" B_PRIuSIZE "\n", area->used_pages);
687 	kprintf("protection cookie: %p\n", area->protection_cookie);
688 	kprintf("lock: %p\n", &area->lock);
689 
690 	size_t freeCount = 0;
691 	void* item = list_get_first_item(&area->free_list);
692 	while (item != NULL) {
693 		freeCount++;
694 
695 		if ((((guarded_heap_page*)item)->flags & GUARDED_HEAP_PAGE_FLAG_USED)
696 				!= 0) {
697 			kprintf("free list broken, page %p not actually free\n", item);
698 		}
699 
700 		item = list_get_next_item(&area->free_list, item);
701 	}
702 
703 	kprintf("free_list: %p (%" B_PRIuSIZE " free)\n", &area->free_list,
704 		freeCount);
705 
706 	freeCount = 0;
707 	size_t runLength = 0;
708 	size_t longestRun = 0;
709 	for (size_t i = 0; i <= area->page_count; i++) {
710 		guarded_heap_page& page = area->pages[i];
711 		if (i == area->page_count
712 			|| (page.flags & GUARDED_HEAP_PAGE_FLAG_USED) != 0) {
713 			freeCount += runLength;
714 			if (runLength > longestRun)
715 				longestRun = runLength;
716 			runLength = 0;
717 			continue;
718 		}
719 
720 		runLength = 1;
721 		for (size_t j = 1; j < area->page_count - i; j++) {
722 			if ((area->pages[i + j].flags & GUARDED_HEAP_PAGE_FLAG_USED) != 0)
723 				break;
724 
725 			runLength++;
726 		}
727 
728 		i += runLength - 1;
729 	}
730 
731 	kprintf("longest free run: %" B_PRIuSIZE " (%" B_PRIuSIZE " free)\n",
732 		longestRun, freeCount);
733 
734 	kprintf("pages: %p\n", area->pages);
735 
736 	return 0;
737 }
738 
739 
740 static int
741 dump_guarded_heap(int argc, char** argv)
742 {
743 	guarded_heap* heap = &sGuardedHeap;
744 	if (argc != 1) {
745 		if (argc == 2)
746 			heap = (guarded_heap*)parse_expression(argv[1]);
747 		else {
748 			print_debugger_command_usage(argv[0]);
749 			return 0;
750 		}
751 	}
752 
753 	kprintf("guarded heap: %p\n", heap);
754 	kprintf("rw lock: %p\n", &heap->lock);
755 	kprintf("page count: %" B_PRIuSIZE "\n", heap->page_count);
756 	kprintf("used pages: %" B_PRIuSIZE "\n", heap->used_pages);
757 	kprintf("area creation counter: %" B_PRId32 "\n",
758 		heap->area_creation_counter);
759 
760 	size_t areaCount = 0;
761 	guarded_heap_area* area = heap->areas;
762 	while (area != NULL) {
763 		areaCount++;
764 		area = area->next;
765 	}
766 
767 	kprintf("areas: %p (%" B_PRIuSIZE ")\n", heap->areas, areaCount);
768 
769 	return 0;
770 }
771 
772 
773 // #pragma mark - Malloc API
774 
775 
776 status_t
777 heap_init(addr_t address, size_t size)
778 {
779 	return guarded_heap_area_init(sGuardedHeap, -1, (void*)address, size, 0)
780 		? B_OK : B_ERROR;
781 }
782 
783 
784 status_t
785 heap_init_post_area()
786 {
787 	return B_OK;
788 }
789 
790 
791 status_t
792 heap_init_post_sem()
793 {
794 	for (guarded_heap_area* area = sGuardedHeap.areas; area != NULL;
795 			area = area->next) {
796 		if (area->area >= 0)
797 			continue;
798 
799 		area_id id = area_for((void*)area->base);
800 		if (id < 0 || vm_prepare_kernel_area_debug_protection(id,
801 				&area->protection_cookie) != B_OK) {
802 			panic("failed to prepare initial guarded heap for protection");
803 			continue;
804 		}
805 
806 		area->area = id;
807 		for (size_t i = 0; i < area->page_count; i++) {
808 			guarded_heap_page& page = area->pages[i];
809 			if ((page.flags & GUARDED_HEAP_PAGE_FLAG_USED) != 0
810 				&& (page.flags & GUARDED_HEAP_PAGE_FLAG_GUARD) == 0
811 				&& (page.flags & GUARDED_HEAP_PAGE_FLAG_DEAD) == 0) {
812 				guarded_heap_page_protect(*area, i,
813 					B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
814 			} else
815 				guarded_heap_page_protect(*area, i, 0);
816 		}
817 	}
818 
819 	add_debugger_command("guarded_heap", &dump_guarded_heap,
820 		"Dump info about the guarded heap");
821 	add_debugger_command_etc("guarded_heap_area", &dump_guarded_heap_area,
822 		"Dump info about a guarded heap area",
823 		"<address>\nDump info about guarded heap area containing address.\n",
824 		0);
825 	add_debugger_command_etc("guarded_heap_page", &dump_guarded_heap_page,
826 		"Dump info about a guarded heap page",
827 		"<address>\nDump info about guarded heap page containing address.\n",
828 		0);
829 
830 	return B_OK;
831 }
832 
833 
834 void*
835 memalign(size_t alignment, size_t size)
836 {
837 	return memalign_etc(alignment, size, 0);
838 }
839 
840 
841 void *
842 memalign_etc(size_t alignment, size_t size, uint32 flags)
843 {
844 	if (size == 0)
845 		size = 1;
846 
847 	return guarded_heap_allocate(sGuardedHeap, size, alignment, flags);
848 }
849 
850 
851 void
852 free_etc(void *address, uint32 flags)
853 {
854 	guarded_heap_free(address, flags);
855 }
856 
857 
858 void*
859 malloc(size_t size)
860 {
861 	return memalign_etc(0, size, 0);
862 }
863 
864 
865 void
866 free(void* address)
867 {
868 	free_etc(address, 0);
869 }
870 
871 
872 void*
873 realloc(void* address, size_t newSize)
874 {
875 	if (newSize == 0) {
876 		free(address);
877 		return NULL;
878 	}
879 
880 	if (address == NULL)
881 		return memalign(0, newSize);
882 
883 	return guarded_heap_realloc(address, newSize);
884 }
885 
886 
887 #if USE_GUARDED_HEAP_FOR_OBJECT_CACHE
888 
889 
890 // #pragma mark - Slab API
891 
892 
893 void
894 request_memory_manager_maintenance()
895 {
896 }
897 
898 
899 object_cache*
900 create_object_cache(const char*, size_t objectSize, size_t, void*,
901 	object_cache_constructor, object_cache_destructor)
902 {
903 	return (object_cache*)objectSize;
904 }
905 
906 
907 object_cache*
908 create_object_cache_etc(const char*, size_t objectSize, size_t, size_t, size_t,
909 	size_t, uint32, void*, object_cache_constructor, object_cache_destructor,
910 	object_cache_reclaimer)
911 {
912 	return (object_cache*)objectSize;
913 }
914 
915 
916 void
917 delete_object_cache(object_cache* cache)
918 {
919 }
920 
921 
922 status_t
923 object_cache_set_minimum_reserve(object_cache* cache, size_t objectCount)
924 {
925 	return B_OK;
926 }
927 
928 
929 void*
930 object_cache_alloc(object_cache* cache, uint32 flags)
931 {
932 	return memalign_etc(0, (size_t)cache, flags);
933 }
934 
935 
936 void
937 object_cache_free(object_cache* cache, void* object, uint32 flags)
938 {
939 	return free_etc(object, flags);
940 }
941 
942 
943 status_t
944 object_cache_reserve(object_cache* cache, size_t objectCount, uint32 flags)
945 {
946 	return B_OK;
947 }
948 
949 
950 void
951 object_cache_get_usage(object_cache* cache, size_t* _allocatedMemory)
952 {
953 	*_allocatedMemory = 0;
954 }
955 
956 
957 void
958 slab_init(kernel_args* args)
959 {
960 }
961 
962 
963 void
964 slab_init_post_area()
965 {
966 }
967 
968 
969 void
970 slab_init_post_sem()
971 {
972 }
973 
974 
975 void
976 slab_init_post_thread()
977 {
978 }
979 
980 
981 #endif	// USE_GUARDED_HEAP_FOR_OBJECT_CACHE
982 
983 
984 #endif	// USE_GUARDED_HEAP_FOR_MALLOC
985