Loading...
--- /dev/null
+++ libmalloc/libmalloc-657.80.3/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__)