Loading...
--- /dev/null
+++ libmalloc/libmalloc-792.60.6/tests/malloc_create_zone_tests.c
@@ -0,0 +1,564 @@
+//
+// malloc_create_xzone_tests.c
+// libmalloc
+//
+// Tests that we can create additional xzone malloc zones through
+// malloc_create_zone, and that these zones behave properly
+//
+
+#include <darwintest.h>
+#include <stdlib.h>
+#include <malloc/malloc.h>
+#include <../src/internal.h>
+
+T_DECL(malloc_create_many_zones, "Register lots of zones",
+ T_META_CHECK_LEAKS(false), T_META_TAG_ALL_ALLOCATORS,
+ T_META_TAG_VM_NOT_PREFERRED, T_META_ENABLED(MALLOC_TARGET_64BIT))
+{
+ const int num_zones = 4097;
+
+ for (int i = 0; i < num_zones; i++) {
+ malloc_zone_t *zone = malloc_create_zone(0, 0);
+ T_QUIET; T_ASSERT_NOTNULL(zone, "create zone %d", i);
+
+ void *p = malloc_zone_malloc(zone, 16);
+ T_QUIET; T_ASSERT_NOTNULL(p, "malloc_zone_malloc %d", i);
+ T_QUIET; T_ASSERT_GE(malloc_size(p), 16ul, "malloc_size %d", i);
+ free(p);
+ }
+}
+
+#if CONFIG_XZONE_MALLOC
+
+#define PTR_EQUALS(a, b) (((uintptr_t)a & (XZM_LIMIT_ADDRESS - 1)) == \
+ ((uintptr_t)b & (XZM_LIMIT_ADDRESS - 1)))
+
+T_DECL(malloc_xzone_create_zone, "Test malloc_create_zone with xzones enabled",
+ T_META_ENVVAR("MallocProbGuard=0"),
+ T_META_TAG_XZONE_ONLY, T_META_TAG_VM_PREFERRED)
+{
+ // Check that we can create a new zone, and that it is an xzone
+ malloc_zone_t *new_zone = malloc_create_zone(0, 0);
+ T_ASSERT_NOTNULL(new_zone, "Create new zone");
+
+ T_ASSERT_GE(new_zone->version, 14, "New zone supports zone_type");
+ T_ASSERT_EQ(new_zone->introspect->zone_type, MALLOC_ZONE_TYPE_XZONE,
+ "New zone is of type XZONE");
+
+ void *ptr = malloc_zone_malloc(new_zone, 65536);
+ T_ASSERT_NOTNULL(ptr, "allocate from new zone");
+ T_ASSERT_GE(malloc_size(ptr), 65536ul, "pointer size works");
+
+ malloc_destroy_zone(new_zone);
+
+ // Once again, but make a small enough allocation to be served from the
+ // early allocator
+ new_zone = malloc_create_zone(0, 0);
+ T_ASSERT_NOTNULL(new_zone, "Create new zone");
+ T_ASSERT_GE(new_zone->version, 14, "New zone supports zone_type");
+ T_ASSERT_EQ(new_zone->introspect->zone_type, 1,
+ "New zone is of type XZONE");
+ ptr = malloc_zone_malloc(new_zone, 64);
+ T_ASSERT_NOTNULL(ptr, "allocate from new zone");
+ T_ASSERT_GE(malloc_size(ptr), 64ul, "pointer size works");
+ malloc_destroy_zone(new_zone);
+ T_ASSERT_EQ(malloc_size(ptr), 0ul, "pointer is freed by destroying zone");
+ T_PASS("success");
+}
+
+// This test would exhaust the 64GB embedded address space, so only run on OSX
+#if TARGET_OS_OSX
+T_DECL(malloc_xzone_too_many_zones, "Register more zones than xzm supports",
+ T_META_ENVVAR("MallocProbGuard=0"),
+ T_META_TAG_XZONE_ONLY, T_META_TAG_VM_PREFERRED)
+{
+ // XZM uses a uint16_t for the mzone unique ID, so we can only register
+ // (1 << 16) - 2 new zones (ID 1 is reserved for the main zone, and ID 0 is
+ // a magic invalid value). After running out of IDs, check that
+ // malloc_create_zone falls back to a scalable zone
+ const size_t num_xzone_zones = (1 << 16) - 2;
+ malloc_zone_t *xzm_zones[num_xzone_zones];
+ for (int i = 0; i < num_xzone_zones; i++) {
+ xzm_zones[i] = malloc_create_zone(0, 0);
+ T_QUIET; T_ASSERT_NOTNULL(xzm_zones[i], "Create new zone %d", i);
+ T_QUIET;
+ T_ASSERT_GE(xzm_zones[i]->version, 14, "New zone supports zone_type");
+ T_QUIET;
+ T_ASSERT_EQ(xzm_zones[i]->introspect->zone_type, MALLOC_ZONE_TYPE_XZONE,
+ "New zone is of type XZONE");
+ }
+
+ malloc_zone_t *szone = malloc_create_zone(0, 0);
+ T_ASSERT_NOTNULL(szone, "Fallback to new scalable zone");
+ if (szone->version >= 14) {
+ T_ASSERT_NE(szone->introspect->zone_type, MALLOC_ZONE_TYPE_XZONE,
+ "Fallback shouldn't be xzone malloc zone");
+ } else {
+ T_LOG("Fallback malloc zone doesn't support zone_type");
+ }
+
+ malloc_destroy_zone(szone);
+ for (int i = 0; i < num_xzone_zones; i++) {
+ malloc_destroy_zone(xzm_zones[i]);
+ }
+}
+#endif // TARGET_OS_OSX
+
+struct pointers_expected {
+ size_t num_pointers;
+ vm_range_t ranges[0];
+};
+
+// Once we find an enumerated pointer, set the expected size of it to the
+// maximum value so that re-enumerating it causes an error
+const vm_size_t already_seen = (vm_size_t)-1;
+
+static void
+pointer_recorder(task_t task, void *context, unsigned type, vm_range_t *ranges,
+ unsigned count)
+{
+ T_QUIET; T_ASSERT_NOTNULL(context, "Received expected pointer ranges");
+ struct pointers_expected *expected = context;
+
+ for (unsigned i = 0; i < count; i++) {
+ if (!(type & MALLOC_PTR_IN_USE_RANGE_TYPE)) {
+ continue;
+ }
+ // Make sure that this pointer is in the expected list
+ bool found_ptr = false;
+ for (int j = 0; j < expected->num_pointers; j++) {
+ if (PTR_EQUALS(expected->ranges[j].address, ranges[i].address)) {
+ T_QUIET; T_ASSERT_NE(already_seen, expected->ranges[j].size,
+ "enumerated %p twice", (void *)ranges[i].address);
+ T_ASSERT_LE(expected->ranges[j].size, ranges[i].size,
+ "Allocation %i has correct size", j);
+ // Set the size to the max value so that enumerating it twice
+ expected->ranges[j].size = already_seen;
+ found_ptr = true;
+ }
+ }
+ T_QUIET;
+ T_ASSERT_TRUE(found_ptr, "Enumerated unexpected_ptr %p (size 0x%lx)",
+ (void *)ranges[i].address, ranges[i].size);
+ }
+}
+
+static kern_return_t
+memory_reader(task_t remote_task, vm_address_t remote_address, vm_size_t size,
+ void **local_memory)
+{
+ if (local_memory) {
+ *local_memory = (void*)remote_address;
+ return KERN_SUCCESS;
+ }
+ return KERN_FAILURE;
+}
+
+T_DECL(malloc_new_xzone_enumerate, "Test non-default xzone enumerator",
+ T_META_ENVVAR("MallocProbGuard=0"),
+ T_META_TAG_XZONE_ONLY, T_META_TAG_VM_PREFERRED)
+{
+ malloc_zone_t *new_zone = malloc_create_zone(0, 0);
+ T_ASSERT_GE(new_zone->version, 14, "New zone isn't an xzone");
+ T_ASSERT_EQ(new_zone->introspect->zone_type, MALLOC_ZONE_TYPE_XZONE,
+ "New zone isn't an xzone");
+
+ // Make 4 tiny allocations, 2 small allocations, 1 large and 1 huge
+ // allocation from the new zone, and assert that the enumerator sees them
+ const size_t num_pointers = 8;
+ struct pointers_expected *pointers = malloc(sizeof(*pointers) +
+ num_pointers * sizeof(vm_range_t));
+ T_ASSERT_NOTNULL(pointers, "Allocated space for expected ranges");
+ pointers->num_pointers = num_pointers;
+ pointers->ranges[0].size = 64;
+ pointers->ranges[1].size = 256;
+ pointers->ranges[2].size = 1024;
+ pointers->ranges[3].size = 2048; // TINY
+ pointers->ranges[4].size = 10000;
+ pointers->ranges[5].size = 30000; // SMALL
+ pointers->ranges[6].size = 65536; // LARGE
+ pointers->ranges[7].size = 4000000; // HUGE
+
+ for (int i = 0; i < num_pointers; i++) {
+ size_t size = pointers->ranges[i].size;
+ void *addr = malloc_zone_malloc(new_zone, size);
+ T_ASSERT_NOTNULL(addr, "Malloc %zu bytes from new zone", size);
+ pointers->ranges[i].address = (mach_vm_address_t)addr;
+ }
+
+ // Run the enumerator
+ kern_return_t kr = new_zone->introspect->enumerator(mach_task_self(),
+ pointers, MALLOC_PTR_IN_USE_RANGE_TYPE, (vm_address_t)new_zone,
+ memory_reader, pointer_recorder);
+ T_ASSERT_EQ(KERN_SUCCESS, kr, "Enumerator returned success");
+
+ // Check that all pointers were seen
+ for (int i = 0; i < pointers->num_pointers; i++) {
+ // The enumerator sets the size in this structure for every address
+ // it sees to already_seen
+ T_ASSERT_EQ(already_seen, pointers->ranges[i].size, "Found pointer %i",
+ i);
+ }
+
+ // Free all pointers to pass the leaks check
+ for (int i = 0; i < num_pointers; i++) {
+ free((void*)pointers->ranges[i].address);
+ }
+ free(pointers);
+}
+
+struct worker_thread_data {
+ bool done;
+ malloc_zone_t *zone;
+};
+static void *
+worker_thread(void *arg)
+{
+ struct worker_thread_data *data = (struct worker_thread_data*)arg;
+ bool *done = &data->done;
+ malloc_zone_t *zone = data->zone;
+ while (!os_atomic_load(done, relaxed)) {
+ size_t size = 1 << (rand() % 16);
+ void *ptr = malloc_zone_malloc(zone, size);
+ T_QUIET; T_ASSERT_NOTNULL(ptr, "malloc failed");
+ free(ptr);
+ }
+ return NULL;
+}
+
+T_DECL(malloc_fork_with_xzone, "Test that we can fork with a non-default xzone",
+ T_META_ENVVAR("MallocProbGuard=0"),
+ T_META_TAG_XZONE_ONLY, T_META_TAG_VM_NOT_PREFERRED)
+{
+ malloc_zone_t *new_zone = malloc_create_zone(0, 0);
+ T_ASSERT_GE(new_zone->version, 14, "New zone isn't an xzone");
+ T_ASSERT_EQ(new_zone->introspect->zone_type, MALLOC_ZONE_TYPE_XZONE,
+ "New zone isn't an xzone");
+
+ // Create some worker threads to do constant allocations and frees, to try
+ // to expose any multi-threading bugs
+ struct worker_thread_data data = {
+ .done = false,
+ .zone = new_zone,
+ };
+ const int num_threads = 8;
+ pthread_t worker_threads[8] = { NULL };
+ for (int i = 0; i < num_threads; i++) {
+ pthread_create(&worker_threads[i], NULL, &worker_thread, &data);
+ }
+
+ // Wait a few microseconds so that the worker threads have had a chance to
+ // run
+ usleep(100);
+
+ // Make 2 allocations, fork, and then ensure that both can see the
+ // allocations and free them
+ void *ptr1 = malloc_zone_malloc(new_zone, 64);
+ void *ptr2 = malloc_zone_malloc(new_zone, 65536);
+ T_ASSERT_NOTNULL(ptr1, "TINY allocation");
+ T_ASSERT_NOTNULL(ptr2, "LARGE allocation");
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ // Child - check that the pointers exist and can be freed, and exit
+ // nonzero if we run into any problems
+ if (malloc_size(ptr1) < 64) {
+ fprintf(stderr, "TINY ptr %p has unexpected size %zu after fork",
+ ptr1, malloc_size(ptr1));
+ exit(1);
+ } else if (malloc_size(ptr2) < 65536) {
+ fprintf(stderr, "LARGE ptr %p has unexpected size %zu after fork",
+ ptr2, malloc_size(ptr2));
+ exit(2);
+ }
+ free(ptr1);
+ free(ptr2);
+ // Xzone Malloc can mistakenly report tiny freed chunks as allocated if
+ // the chunk is madvised, treat a malloc_size mismatch as non-fatal
+ if (malloc_size(ptr1) != 0 && !malloc_engaged_secure_allocator()) {
+ fprintf(stderr, "TINY ptr %p has unexpected size %zu after free",
+ ptr1, malloc_size(ptr1));
+ exit(3);
+ } else if (malloc_size(ptr2) != 0) {
+ fprintf(stderr, "LARGE ptr %p has unexpected size %zu after free",
+ ptr2, malloc_size(ptr2));
+ exit(4);
+ } else {
+ // Success
+ exit(0);
+ }
+ } else if (pid == -1) {
+ T_ASSERT_FAIL("Fork failed");
+ }
+
+ // Parent
+ int child_status = -1;
+ pid_t child_pid = wait(&child_status);
+ T_ASSERT_EQ(pid, child_pid, "Expected pid from child");
+ T_ASSERT_TRUE(WIFEXITED(child_status), "Child called exit");
+ T_ASSERT_EQ(WEXITSTATUS(child_status), 0, "Child called exit(0)");
+
+ T_ASSERT_LE(malloc_size(ptr1), 64ul, "tiny pointer still seen post fork");
+ T_ASSERT_LE(malloc_size(ptr2), 65536ul, "large pointer still seen post fork");
+
+ free(ptr1);
+ free(ptr2);
+
+ // Join all the worker threads so that there are no leaks when the test ends
+ os_atomic_store(&data.done, true, relaxed);
+ for (int i = 0; i < num_threads; i++) {
+ pthread_join(worker_threads[i], NULL);
+ }
+}
+
+T_DECL(malloc_statistics, "Make sure the main and new zone support statistics",
+ T_META_ENVVAR("MallocProbGuard=0"),
+ T_META_TAG_ALL_ALLOCATORS, T_META_TAG_VM_PREFERRED)
+{
+ malloc_zone_t *default_zone = malloc_default_zone();
+ malloc_zone_t *new_zone = malloc_create_zone(0, 0);
+
+ malloc_statistics_t default_stats = { 0 };
+ malloc_statistics_t new_stats = { 0 };
+
+ malloc_zone_statistics(default_zone, &default_stats);
+ malloc_zone_statistics(new_zone, &new_stats);
+
+ T_ASSERT_EQ(new_stats.size_in_use, 0ul,
+ "Accurate stats before any allocations");
+ T_ASSERT_EQ(new_stats.blocks_in_use, 0u,
+ "Accurate allocation count before any allocations");
+ void *ptr1 = malloc_zone_malloc(new_zone, 1024);
+ void *ptr2 = malloc_zone_malloc(new_zone, 8192);
+ malloc_zone_statistics(new_zone, &new_stats);
+ free(ptr1);
+ free(ptr2);
+
+ T_ASSERT_EQ(new_stats.size_in_use, 1024ul + 8192ul,
+ "Accurate stats after allocations");
+ T_ASSERT_EQ(new_stats.blocks_in_use, 2u,
+ "Accurate allocation count after allocations");
+}
+
+T_DECL(malloc_free_pointers_on_destroy,
+ "Make many allocations, make sure they're freed on zone destroy",
+ T_META_TAG_ALL_ALLOCATORS, T_META_TAG_VM_PREFERRED)
+{
+ malloc_zone_t *new_zone = malloc_create_zone(0, 0);
+
+ // Allocate 1 segment of tiny chunks (4k * 1024 == 4M)
+ void *tiny_ptrs[1024];
+
+ // Allocate 3 segments worth of small chunks (32k * 384 == 4M * 3)
+ void *small_ptrs[384];
+
+ // Allocate 5 segments worth of large chunks
+ void *large_ptrs[256];
+
+ // Allocate 2 huge segments
+ void *huge_ptrs[2];
+
+ for (int i = 0; i < countof(tiny_ptrs); i++) {
+ tiny_ptrs[i] = malloc_zone_malloc(new_zone, 1024);
+ T_QUIET; T_ASSERT_NOTNULL(tiny_ptrs[i], "TINY allocation succeeded");
+ T_QUIET; T_ASSERT_GE(malloc_size(tiny_ptrs[i]), 1024ul,
+ "TINY allocation is of requested size");
+ }
+
+ for (int i = 0; i < countof(small_ptrs); i++) {
+ small_ptrs[i] = malloc_zone_malloc(new_zone, 32768);
+ T_QUIET; T_ASSERT_NOTNULL(small_ptrs[i], "SMALL allocation succeeded");
+ T_QUIET; T_ASSERT_GE(malloc_size(small_ptrs[i]), 32768ul,
+ "SMALL allocation is of requested size");
+ }
+
+ for (int i = 0; i < countof(large_ptrs); i++) {
+ large_ptrs[i] = malloc_zone_malloc(new_zone, 65536);
+ T_QUIET; T_ASSERT_NOTNULL(large_ptrs[i], "LARGE allocation succeeded");
+ T_QUIET; T_ASSERT_GE(malloc_size(large_ptrs[i]), 65536ul,
+ "LARGE allocation is of requested size");
+ }
+
+ for (int i = 0; i < countof(huge_ptrs); i++) {
+ huge_ptrs[i] = malloc_zone_malloc(new_zone, MiB(32));
+ T_QUIET; T_ASSERT_NOTNULL(huge_ptrs[i], "HUGE allocation succeeded");
+ T_QUIET; T_ASSERT_GE(malloc_size(huge_ptrs[i]), (size_t)MiB(32),
+ "HUGE allocation is of requested size");
+ }
+
+ malloc_destroy_zone(new_zone);
+
+ for (int i = 0; i < countof(tiny_ptrs); i++) {
+ T_QUIET; T_ASSERT_EQ(malloc_size(tiny_ptrs[i]), 0ul,
+ "TINY allocation is freed by destroying zone");
+ }
+
+ for (int i = 0; i < countof(small_ptrs); i++) {
+ T_QUIET; T_ASSERT_EQ(malloc_size(small_ptrs[i]), 0ul,
+ "SMALL allocation is freed by destroying zone");
+ }
+
+ for (int i = 0; i < countof(large_ptrs); i++) {
+ T_QUIET; T_ASSERT_EQ(malloc_size(large_ptrs[i]), 0ul,
+ "LARGE allocation is freed by destroying zone");
+ }
+
+ for (int i = 0; i < countof(huge_ptrs); i++) {
+ T_QUIET; T_ASSERT_EQ(malloc_size(huge_ptrs[i]), 0ul,
+ "HUGE allocation is freed by destroying zone");
+ }
+}
+
+static void zone_pressure(bool *finished, malloc_zone_t *zone, bool free)
+{
+ const int num_ptrs = 1024;
+ void *ptrs[num_ptrs] = { NULL };
+
+ malloc_type_id_t pure_data = (malloc_type_descriptor_t){
+ .summary = (malloc_type_summary_t){
+ .layout_semantics = (malloc_type_layout_semantics_t) {
+ .generic_data = true,
+ }
+ }
+ }.type_id;
+
+ while (!os_atomic_load(finished, relaxed)) {
+ bool data = rand() & 0x1;
+ int index = rand() % num_ptrs;
+ int new_sz = (rand() & 0xff) + 1;
+ new_sz <<= (rand() % 10);
+ if (ptrs[index] && ((rand() % 8) == 0)) {
+ if (data) {
+ ptrs[index] = malloc_type_zone_realloc(zone, ptrs[index],
+ new_sz, pure_data);
+ } else {
+ ptrs[index] = malloc_zone_realloc(zone, ptrs[index], new_sz);
+ }
+ } else {
+ malloc_zone_free(zone, ptrs[index]);
+ if (data) {
+ ptrs[index] = malloc_type_zone_malloc(zone, new_sz, pure_data);
+ } else {
+ ptrs[index] = malloc_zone_malloc(zone, new_sz);
+ }
+ }
+ }
+
+ if (free) {
+ for (int i = 0; i < num_ptrs; i++) {
+ malloc_zone_free(zone, ptrs[i]);
+ }
+ }
+}
+
+extern malloc_zone_t **malloc_zones;
+static void *
+main_zone_thread(void *arg)
+{
+ bool *finished = (bool *)arg;
+ malloc_zone_t *zone = malloc_zones[0];
+ zone_pressure(finished, zone, true);
+ return NULL;
+}
+
+static void *
+create_zone_thread(void *arg)
+{
+ bool *finished = (bool *)arg;
+ malloc_zone_t *zone = malloc_create_zone(0, 0);
+ zone_pressure(finished, zone, false);
+ malloc_destroy_zone(zone);
+ return NULL;
+}
+
+static void
+create_zone_stress(void)
+{
+ const int num_main_threads = 3;
+ const int concurrent_new_zones = 3;
+ const int iterations = 100;
+
+ bool main_finished = false;
+ pthread_t main_zone_threads[num_main_threads];
+ for (int i = 0; i < num_main_threads; i++) {
+ int rc = pthread_create(&main_zone_threads[i], NULL, main_zone_thread,
+ &main_finished);
+ T_ASSERT_POSIX_ZERO(rc, "Create main zone pressure thread");
+ }
+
+ sleep(1);
+
+ for (int i = 0; i < iterations; i++) {
+ bool finished = false;
+ pthread_t new_zone_threads[concurrent_new_zones];
+ for (int j = 0; j < concurrent_new_zones; j++) {
+ int rc = pthread_create(&new_zone_threads[j], NULL,
+ create_zone_thread, &finished);
+ T_ASSERT_POSIX_ZERO(rc, "Create new zone thread");
+ usleep(500);
+ }
+
+ usleep(50000);
+ os_atomic_store(&finished, true, relaxed);
+
+ for (int j = 0; j < concurrent_new_zones; j++) {
+ int rc = pthread_join(new_zone_threads[j], NULL);
+ T_ASSERT_POSIX_ZERO(rc, "Join new zone thread");
+ }
+
+ usleep(50000);
+ }
+
+ os_atomic_store(&main_finished, true, relaxed);
+ for (int i = 0; i < num_main_threads; i++) {
+ int rc = pthread_join(main_zone_threads[i], NULL);
+ T_ASSERT_POSIX_ZERO(rc, "Join main thread");
+ }
+
+ // Create new zones so that their mzone indices are enumerated by leaks
+ // after the test runs
+ for (int i = 0; i < concurrent_new_zones; i++) {
+ malloc_create_zone(0, 0);
+ }
+}
+
+T_DECL(malloc_create_zone_stress,
+ "Create and destroy zones while stressing main zone",
+ T_META_ENVVAR("MallocProbGuard=0"),
+ T_META_TAG_ALL_ALLOCATORS, T_META_TAG_VM_NOT_PREFERRED)
+{
+ create_zone_stress();
+}
+
+T_DECL(malloc_create_zone_stress_guarded,
+ "Create and destroy zones while stressing main zone, guard pages enabled",
+ T_META_ENVVAR("MallocXzoneGuarded=1"),
+ T_META_ENVVAR("MallocProbGuard=0"),
+ T_META_TAG_XZONE_ONLY, T_META_TAG_VM_NOT_PREFERRED)
+{
+ create_zone_stress();
+}
+
+T_DECL(free_default_with_scribble,
+ "Test freeing from a non-default zone while MALLOC_SCRIBBLE is enabled",
+ T_META_TAG_ALL_ALLOCATORS,
+ T_META_ENVVAR("MallocScribble=1"), T_META_TAG_VM_NOT_PREFERRED)
+{
+ malloc_zone_t *zone = malloc_create_zone(0, 0);
+ void *ptr = malloc_zone_malloc(zone, 16384);
+ T_ASSERT_NOTNULL(ptr, "Allocate from non-default zone");
+ free(ptr);
+ T_ASSERT_EQ(malloc_size(ptr), 0ul, "Pointer is freed");
+}
+
+#else // CONFIG_XZONE_MALLOC
+
+T_DECL(skip_create_zone, "Do nothing test for !CONFIG_XZONE_MALLOC",
+ T_META_ENABLED(false), T_META_TAG_VM_PREFERRED,
+ T_META_TAG_NO_ALLOCATOR_OVERRIDE)
+{
+ T_SKIP("Nothing to test under !CONFIG_XZONE_MALLOC");
+
+}
+
+#endif // CONFIG_XZONE_MALLOC