Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | // // posix_memalign_test.c // libmalloc // // test allocating and freeing all sizes and alignments // #include <darwintest.h> #include <errno.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <mach/mach.h> #include <malloc/malloc.h> #include <malloc_private.h> T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true), T_META_TAG_XZONE); struct t_recorder_ctx { void *ptr; size_t size; bool found; }; static void pointer_recorder(task_t task, void *context, unsigned type, vm_range_t *ranges, unsigned count) { if (!(type & MALLOC_PTR_IN_USE_RANGE_TYPE)) { return; } struct t_recorder_ctx *ctx = context; vm_address_t ptr_addr = (vm_address_t)(ctx->ptr); vm_size_t ptr_size = (vm_size_t)(ctx->size); for (unsigned i = 0; i < count; i++) { vm_range_t *range = &ranges[i]; if (range->address <= ptr_addr && range->address + range->size > ptr_addr) { T_QUIET; T_EXPECT_FALSE(ctx->found, "first time"); vm_size_t offset = ptr_addr - range->address; T_QUIET; T_EXPECT_GE(range->size - offset, ctx->size, "allocation must be large enough"); ctx->found = true; } } } static void check_pointer_is_enumerated(void *ptr, size_t size) { vm_address_t *zones; unsigned zone_count; kern_return_t kr; kr = malloc_get_all_zones(mach_task_self(), /*reader=*/NULL, &zones, &zone_count); T_QUIET; T_ASSERT_EQ(kr, KERN_SUCCESS, "malloc_get_all_zones(mach_task_self(), ...)"); struct t_recorder_ctx ctx = { .ptr = ptr, .size = size, .found = false, }; for (unsigned i = 0; i < zone_count; i++) { malloc_zone_t *zone = (malloc_zone_t *)zones[i]; zone->introspect->enumerator(mach_task_self(), &ctx, MALLOC_PTR_IN_USE_RANGE_TYPE, (vm_address_t)zone, NULL, pointer_recorder); if (ctx.found) { return; } } T_QUIET; T_FAIL("pointer %p not enumerated in any zone", ptr); } static inline void * t_posix_memalign(size_t alignment, size_t size, bool scribble, bool enumerate) { void *ptr = NULL; int result = posix_memalign(&ptr, alignment, size); size_t allocated_size = malloc_size(ptr); T_QUIET; T_ASSERT_NOTNULL(ptr, "allocation"); T_QUIET; T_ASSERT_EQ((intptr_t)ptr % alignment, 0ul, "pointer should be properly aligned"); T_QUIET; T_EXPECT_LE(size, allocated_size, "allocation size"); T_QUIET; T_EXPECT_TRUE(malloc_claimed_address(ptr), "should be claimed"); if (enumerate) { check_pointer_is_enumerated(ptr, size); } if (scribble) { // Scribble memory pointed to by `ptr` to make sure we're not using that // memory for control structures. This also makes sure the memory can be // written to. const uint64_t pat = 0xdeadbeefcafebabeull; memset_pattern8(ptr, &pat, size); } return ptr; } T_DECL(posix_memalign_free, "posix_memalign all power of two alignments <= 4096") { for (size_t alignment = sizeof(void*); alignment < 4096; alignment *= 2) { bool enumerate = true; // test several sizes for (size_t size = alignment; size <= 256*alignment; size += 8) { void* ptr = t_posix_memalign(alignment, size, true, enumerate); free(ptr); enumerate = false; } } } T_DECL(posix_memalign_alignment_not_a_power_of_2, "posix_memalign should return EINVAL if alignment is not a power of 2") { { void *ptr = NULL; int result = posix_memalign(&ptr, 24, 48); // alignment is even, but not a power of two T_QUIET; T_ASSERT_NULL(ptr, "ptr should be null"); T_QUIET; T_ASSERT_EQ(result, EINVAL, "posix_memalign should return EINVAL"); } { void *ptr = NULL; int result = posix_memalign(&ptr, 23, 46); // alignment is odd, and not a power of two T_QUIET; T_ASSERT_NULL(ptr, "ptr should be null"); T_QUIET; T_ASSERT_EQ(result, EINVAL, "posix_memalign should return EINVAL"); } } T_DECL(posix_memalign_alignment_not_a_multiple_of_voidstar, "posix_memalign should return EINVAL if alignment is not a multiple of sizeof(void*)") { void *ptr = NULL; const size_t alignment = sizeof(void*)+1; int result = posix_memalign(&ptr, alignment, alignment * 2); T_QUIET; T_ASSERT_NULL(ptr, "ptr should be null"); T_QUIET; T_ASSERT_EQ(result, EINVAL, "posix_memalign should return EINVAL"); } T_DECL(posix_memalign_allocate_size_0, "posix_memalign should return something that can be passed to free() when size is 0") { void *ptr = NULL; int result = posix_memalign(&ptr, 8, 0); T_QUIET; T_ASSERT_EQ(result, 0, "posix_memalign should not return an error when asked for size 0"); free(ptr); } #if defined(__LP64__) T_DECL(posix_memalign_large, "posix_memalign large power of two alignments") { // 64GB on macOS, 64MB on embedded uint64_t max_alignment = TARGET_OS_OSX ? UINT64_C(68719476736) : UINT64_C(67108864); for (size_t alignment = sizeof(void*); alignment <= max_alignment; alignment *= 2) { // don't scribble - we don't want to actually touch that many pages, we just // verify that the allocated pointer looks reasonable void* ptr = t_posix_memalign(alignment, alignment, false, true); free(ptr); } T_END; } #endif // __LP64__ |