Loading...
tests/mfm_test.c /dev/null libmalloc-657.60.21
--- /dev/null
+++ libmalloc/libmalloc-657.60.21/tests/mfm_test.c
@@ -0,0 +1,381 @@
+#define MFM_TESTING 1
+#include <malloc/malloc.h>
+#include <stdlib.h>
+#include <darwintest.h>
+
+#if defined(__LP64__)
+
+#include "../src/internal.h" // MALLOC_TARGET_EXCLAVES
+
+#if !MALLOC_TARGET_EXCLAVES
+#include <mach/mach_init.h>
+#include <mach/vm_map.h>
+#endif // !MALLOC_TARGET_EXCLAVES
+
+T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true), T_META_TAG_VM_PREFERRED);
+
+static void *
+test_mvm_allocate_pages(size_t size, int vm_page_label, plat_map_t *map_out)
+{
+#if MALLOC_TARGET_EXCLAVES
+	return mmap_plat(map_out, 0, size,
+			LIBLIBC_MAP_PERM_READ | LIBLIBC_MAP_PERM_WRITE,
+			LIBLIBC_MAP_TYPE_PRIVATE, 0, vm_page_label);
+#else
+	vm_address_t vm_addr;
+	kern_return_t kr;
+
+	kr = vm_allocate(mach_task_self(), &vm_addr, size,
+			VM_FLAGS_ANYWHERE | VM_MAKE_TAG(vm_page_label));
+	return kr == KERN_SUCCESS ? (void *)vm_addr : NULL;
+#endif // MALLOC_TARGET_EXCLAVES
+}
+#define mvm_allocate_pages_plat(size, align, debug_flags, vm_page_label, plat) \
+	test_mvm_allocate_pages(size, vm_page_label, plat)
+
+static void *
+test_mvm_allocate_plat(uintptr_t addr, size_t size, int flags,
+		uint32_t debug_flags, int vm_page_label, plat_map_t *map_out)
+{
+#if MALLOC_TARGET_EXCLAVES
+	const _liblibc_map_type_t type = LIBLIBC_MAP_TYPE_PRIVATE |
+			((flags & VM_FLAGS_ANYWHERE) ? 0 : LIBLIBC_MAP_TYPE_FIXED) |
+			((debug_flags & MALLOC_NO_POPULATE) ? LIBLIBC_MAP_TYPE_NOCOMMIT : 0);
+	return mmap_plat(map_out, addr, size,
+			LIBLIBC_MAP_PERM_READ | LIBLIBC_MAP_PERM_WRITE, type, 0,
+			(unsigned)vm_page_label);
+#else
+	vm_address_t vm_addr;
+	kern_return_t kr;
+
+	kr = vm_allocate(mach_task_self(), &vm_addr, size,
+			VM_FLAGS_ANYWHERE | VM_MAKE_TAG(vm_page_label));
+	return kr == KERN_SUCCESS ? (void *)vm_addr : NULL;
+#endif // MALLOC_TARGET_EXCLAVES
+}
+#define mvm_allocate_plat(addr, size, align, flags, debug_flags, vm_page_label, plat) \
+	test_mvm_allocate_plat(addr, size, flags, debug_flags, vm_page_label, plat)
+
+static void test_malloc_lock_lock(_malloc_lock_s *lock) {
+#if MALLOC_HAS_OS_LOCK
+	os_unfair_lock_lock(lock);
+#else
+	T_QUIET; T_ASSERT_EQ(pthread_mutex_lock(lock), 0, "Lock lock");
+#endif // MALLOC_HAS_OS_LOCK
+}
+#define _malloc_lock_lock(lock) test_malloc_lock_lock(lock);
+
+static void test_malloc_lock_unlock(_malloc_lock_s *lock) {
+#if MALLOC_HAS_OS_LOCK
+	os_unfair_lock_unlock(lock);
+#else
+	T_QUIET; T_ASSERT_EQ(pthread_mutex_unlock(lock), 0, "Unlock lock");
+#endif // MALLOC_HAS_OS_LOCK
+}
+#define _malloc_lock_unlock(lock) test_malloc_lock_unlock(lock);
+
+#include "../src/early_malloc.c"
+
+T_DECL(mfm_basic, "mfm basic tests")
+{
+	void *ptrs[10];
+	size_t index, size;
+
+	mfm_initialize();
+
+	T_ASSERT_NOTNULL(mfm_arena, "mfm_initialize worked");
+
+	T_ASSERT_NULL(mfm_alloc(MFM_ALLOC_SIZE_MAX + 1),
+	   "allocations larger than %zd bytes should fail",
+	   MFM_ALLOC_SIZE_MAX);
+
+	T_ASSERT_EQ(0ul, mfm_arena->mfmh_bump * MFM_QUANTUM,
+	    "The bump should be at 0");
+
+	ptrs[0] = mfm_alloc(10);
+	T_EXPECT_EQ(16ul, mfm_alloc_size(ptrs[0]),
+	    "allocation should be 16 bytes");
+	index = __mfm_block_index(mfm_arena, ptrs[0]);
+	size  = __mfm_block_size(mfm_arena, index);
+	T_QUIET; T_EXPECT_TRUE(__mfm_block_is_allocated(mfm_arena, index), "allocated");
+	T_QUIET; T_EXPECT_TRUE(!__mfm_block_is_allocated(mfm_arena, index + size), "allocated");
+	T_QUIET; T_EXPECT_TRUE(__mfm_prev_block_is_allocated(mfm_arena, index + size), "allocated");
+
+	ptrs[1] = mfm_alloc(10);
+	T_EXPECT_EQ(16ul, mfm_alloc_size(ptrs[1]),
+	    "allocation should be 16 bytes");
+	index = __mfm_block_index(mfm_arena, ptrs[1]);
+	size  = __mfm_block_size(mfm_arena, index);
+	T_QUIET; T_EXPECT_TRUE(__mfm_block_is_allocated(mfm_arena, index), "allocated");
+	T_QUIET; T_EXPECT_TRUE(!__mfm_block_is_allocated(mfm_arena, index + size), "allocated");
+	T_QUIET; T_EXPECT_TRUE(__mfm_prev_block_is_allocated(mfm_arena, index + size), "allocated");
+
+	ptrs[2] = mfm_alloc(128);
+	T_EXPECT_EQ(128ul, mfm_alloc_size(ptrs[2]),
+	    "allocation should be 128 bytes");
+	index = __mfm_block_index(mfm_arena, ptrs[2]);
+	size  = __mfm_block_size(mfm_arena, index);
+	T_QUIET; T_EXPECT_TRUE(__mfm_block_is_allocated(mfm_arena, index), "allocated");
+	T_QUIET; T_EXPECT_TRUE(!__mfm_block_is_allocated(mfm_arena, index + size), "allocated");
+	T_QUIET; T_EXPECT_TRUE(__mfm_prev_block_is_allocated(mfm_arena, index + size), "allocated");
+
+	ptrs[3] = mfm_alloc(1024);
+	T_EXPECT_EQ(1024ul, mfm_alloc_size(ptrs[3]),
+	    "allocation should be 1024 bytes");
+	index = __mfm_block_index(mfm_arena, ptrs[3]);
+	size  = __mfm_block_size(mfm_arena, index);
+	T_QUIET; T_EXPECT_TRUE(__mfm_block_is_allocated(mfm_arena, index), "allocated");
+	T_QUIET; T_EXPECT_TRUE(!__mfm_block_is_allocated(mfm_arena, index + size), "allocated");
+	T_QUIET; T_EXPECT_TRUE(__mfm_prev_block_is_allocated(mfm_arena, index + size), "allocated");
+
+	T_ASSERT_EQ(1184ul, mfm_arena->mfmh_bump * MFM_QUANTUM,
+	    "The bump should be at 1184");
+
+	mfm_free(ptrs[1]);
+	mfm_free(ptrs[2]);
+
+	index = __mfm_block_index(mfm_arena, ptrs[1]);
+	size  = __mfm_block_size(mfm_arena, index);
+	T_EXPECT_EQ(size * MFM_QUANTUM, 144ul, "freed block should be 144 bytes");
+	T_QUIET; T_EXPECT_TRUE(!__mfm_block_is_allocated(mfm_arena, index), "allocated");
+	T_QUIET; T_EXPECT_TRUE(__mfm_block_is_allocated(mfm_arena, index + size), "allocated");
+
+	ptrs[4] = mfm_alloc(64);
+	T_EXPECT_EQ(64ul, mfm_alloc_size(ptrs[4]),
+	    "allocation should be 64 bytes");
+	index = __mfm_block_index(mfm_arena, ptrs[4]);
+	size  = __mfm_block_size(mfm_arena, index);
+	T_QUIET; T_EXPECT_TRUE(__mfm_block_is_allocated(mfm_arena, index), "allocated");
+	T_QUIET; T_EXPECT_TRUE(!__mfm_block_is_allocated(mfm_arena, index + size), "allocated");
+	T_QUIET; T_EXPECT_TRUE(__mfm_prev_block_is_allocated(mfm_arena, index + size), "allocated");
+
+	index += size;
+	size   = __mfm_block_size(mfm_arena, index);
+	T_EXPECT_EQ(size * MFM_QUANTUM, 80ul, "freed block should be 80 bytes");
+	T_QUIET; T_EXPECT_TRUE(!__mfm_block_is_allocated(mfm_arena, index), "allocated");
+	T_QUIET; T_EXPECT_TRUE(__mfm_block_is_allocated(mfm_arena, index + size), "allocated");
+
+	mfm_free(ptrs[3]);
+
+	T_ASSERT_EQ(80ul, mfm_arena->mfmh_bump * MFM_QUANTUM,
+	    "The bump should be at 80");
+
+	ptrs[5] = mfm_alloc(64);
+	T_EXPECT_EQ(64ul, mfm_alloc_size(ptrs[5]),
+	    "allocation should be 64 bytes");
+	index = __mfm_block_index(mfm_arena, ptrs[5]);
+	size  = __mfm_block_size(mfm_arena, index);
+	T_QUIET; T_EXPECT_TRUE(__mfm_block_is_allocated(mfm_arena, index), "allocated");
+	T_QUIET; T_EXPECT_TRUE(!__mfm_block_is_allocated(mfm_arena, index + size), "allocated");
+	T_QUIET; T_EXPECT_TRUE(__mfm_prev_block_is_allocated(mfm_arena, index + size), "allocated");
+
+	T_ASSERT_EQ(144ul, mfm_arena->mfmh_bump * MFM_QUANTUM,
+	    "The bump should be at 144");
+
+	print_mfm_arena(mfm_arena, true, (print_task_printer_t *)printf);
+}
+
+T_DECL(mfm_bits, "mfm bitmaps tests")
+{
+	mfm_initialize();
+
+	T_ASSERT_NOTNULL(mfm_arena, "mfm_initialize worked");
+
+	for (size_t n = 0; n < 2 * 128 * 128; n++) {
+		size_t i, j, k, t;
+		void *ptrs[3];
+		size_t idx[3];
+
+		t = n;
+		i = 1 + (t % 128); t /= 128;
+		j = 1 + (t % 128); t /= 128;
+		k = t;
+
+		ptrs[0] = mfm_alloc(i * MFM_QUANTUM);
+		ptrs[1] = mfm_alloc(j * MFM_QUANTUM);
+		ptrs[2] = mfm_alloc(MFM_QUANTUM);
+		idx[0]  = __mfm_block_index(mfm_arena, ptrs[0]);
+		idx[1]  = __mfm_block_index(mfm_arena, ptrs[1]);
+		idx[2]  = __mfm_block_index(mfm_arena, ptrs[2]);
+
+		T_QUIET; T_ASSERT_EQ(i, __mfm_block_size(mfm_arena, idx[0]), NULL);
+		T_QUIET; T_ASSERT_EQ(i, __mfm_prev_block_size(mfm_arena, idx[1]), NULL);
+
+		T_QUIET; T_ASSERT_EQ(j, __mfm_block_size(mfm_arena, idx[1]), NULL);
+		T_QUIET; T_ASSERT_EQ(j, __mfm_prev_block_size(mfm_arena, idx[2]), NULL);
+
+		mfm_free(ptrs[k]);
+
+		T_QUIET; T_ASSERT_EQ(i, __mfm_block_size(mfm_arena, idx[0]), NULL);
+		T_QUIET; T_ASSERT_EQ(i, __mfm_prev_block_size(mfm_arena, idx[1]), NULL);
+
+		T_QUIET; T_ASSERT_EQ(j, __mfm_block_size(mfm_arena, idx[1]), NULL);
+		T_QUIET; T_ASSERT_EQ(j, __mfm_prev_block_size(mfm_arena, idx[2]), NULL);
+
+		mfm_free(ptrs[1 - k]);
+
+		T_QUIET; T_ASSERT_EQ(i + j, __mfm_block_size(mfm_arena, idx[0]), NULL);
+		T_QUIET; T_ASSERT_EQ(i + j, __mfm_prev_block_size(mfm_arena, idx[2]), NULL);
+
+		mfm_free(ptrs[2]);
+
+		T_ASSERT_EQ(mfm_arena->mfmh_bump, 0ul,
+		    "alloc(%zd * 16), alloc(%zd * 16), mfm_alloc(16), free(%zd), free(%zd))",
+		    i, j, k, 1 - k);
+	}
+}
+
+
+struct record {
+	ssize_t   size;
+	uintptr_t addr;
+};
+
+struct alloc_state {
+	size_t         count;
+	struct record *allocs;
+};
+
+static void
+alloc_add(struct alloc_state *state, size_t size, uintptr_t addr)
+{
+	state->allocs[state->count].size = (ssize_t)size;
+	state->allocs[state->count].addr = addr;
+	state->count++;
+}
+
+static void
+alloc_rm(struct alloc_state *state, uintptr_t addr)
+{
+	for (size_t i = 0; i < state->count; i++) {
+		if (state->allocs[i].addr != addr) {
+			continue;
+		}
+		if (i + 1 <= state->count) {
+			state->allocs[i] = state->allocs[state->count - 1];
+			state->allocs[state->count - 1].addr = 0;
+			state->allocs[state->count - 1].size = 0;
+		}
+		state->count--;
+		return;
+	}
+
+	T_FAIL("Couldn't find address %p", (void *)addr);
+}
+
+static size_t
+block_size(size_t alloc_size)
+{
+	return roundup(alloc_size ?: 1, MFM_QUANTUM);
+}
+
+static void
+run_corruption_test(const struct record *recs, size_t count)
+{
+	uintptr_t rec_base, mfm_base;
+	struct alloc_state state;
+
+	state.count  = 0;
+	state.allocs = calloc(count, sizeof(struct record));
+	T_ASSERT_NOTNULL(state.allocs, "could allocate state");
+
+	mfm_initialize();
+	T_ASSERT_NOTNULL(mfm_arena, "mfm_initialize worked");
+
+	rec_base = recs[0].addr;
+	mfm_base = (uintptr_t)mfm_arena->mfm_blocks;
+
+	for (size_t i = 0; i < count; i++) {
+		const struct record *rec = &recs[i];
+
+		if (rec->size >= 0) {
+			void *ptr = mfm_alloc(rec->size);
+			size_t size = mfm_alloc_size(ptr);
+			size_t want_size = block_size(rec->size);
+
+#if MFM_TRACE
+			T_LOG("[%zd] mfm_alloc(%zd) = %p", i, rec->size, ptr);
+			print_mfm_arena(mfm_arena, true, (print_task_printer_t *)printf);
+#endif
+
+			T_QUIET; T_ASSERT_EQ(want_size, size, "size for %zd", i);
+			T_QUIET; T_ASSERT_EQ((uintptr_t)ptr - mfm_base,
+			   rec->addr - rec_base, "ptr for %zd", i);
+			alloc_add(&state, size, (uintptr_t)ptr);
+		} else {
+			void *ptr = (void *)(rec->addr + mfm_base - rec_base);
+
+#if MFM_TRACE
+			T_LOG("[%zd] mfm_free(%p, %zd)", i, ptr, mfm_alloc_size(ptr));
+#endif
+
+			mfm_free(ptr);
+
+#if MFM_TRACE
+			print_mfm_arena(mfm_arena, true, (print_task_printer_t *)printf);
+#endif
+
+			alloc_rm(&state, (uintptr_t)ptr);
+		}
+
+		for (size_t j = 0; j < state.count; j++) {
+			struct record *record = &state.allocs[j];
+
+			T_QUIET; T_EXPECT_EQ(mfm_alloc_size((void *)record->addr),
+			    block_size(record->size),
+			    "alloc %p is live", (void *)record->addr);
+
+
+		}
+	}
+
+	print_mfm_arena(mfm_arena, true, (print_task_printer_t *)printf);
+	T_PASS("Made it to the end");
+
+	free(state.allocs);
+}
+
+T_DECL(mfm_corruption, "mfm corruption test")
+{
+	static const struct record recs[] = {
+#include "mfm/trace_1.in"
+	};
+
+	run_corruption_test(recs, sizeof(recs) / sizeof(struct record));
+}
+
+T_DECL(mfm_corruption2, "mfm corruption test")
+{
+	static const struct record recs[] = {
+#include "mfm/trace_2.in"
+	};
+
+	run_corruption_test(recs, sizeof(recs) / sizeof(struct record));
+}
+
+T_DECL(mfm_corruption3, "mfm corruption test")
+{
+	static const struct record recs[] = {
+#include "mfm/trace_3.in"
+	};
+
+	run_corruption_test(recs, sizeof(recs) / sizeof(struct record));
+}
+
+T_DECL(mfm_corruption4, "mfm corruption test")
+{
+	static const struct record recs[] = {
+#include "mfm/trace_4.in"
+	};
+
+	run_corruption_test(recs, sizeof(recs) / sizeof(struct record));
+}
+
+#else // defined(__LP64__)
+
+T_DECL(mfm_not_supported, "mfm not supported")
+{
+	T_SKIP("mfm not supported on this platform");
+}
+
+#endif // defined(__LP64__)