Loading...
--- /dev/null
+++ libmalloc/libmalloc-792.41.1/src/malloc_exclaves.c
@@ -0,0 +1,721 @@
+/*
+ * Copyright (c) 2023 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <_liblibc/_asan_runtime.h>
+
+#include "internal.h"
+
+#define MAX_MALLOC_ZONES 2
+
+#define DEFAULT_MALLOC_ZONE_STRING "DefaultXzoneZone"
+#define DEFAULT_SANITIZER_ZONE_STRING "DefaultWrapperSanitizerZone"
+
+MALLOC_NOEXPORT
+unsigned int phys_ncpus = 0;
+
+MALLOC_NOEXPORT
+unsigned int logical_ncpus = 0;
+
+unsigned malloc_num_zones = 0;
+static malloc_zone_t *_malloc_zones[MAX_MALLOC_ZONES] = { NULL };
+malloc_zone_t ** __unsafe_indexable malloc_zones = _malloc_zones;
+
+bool malloc_sanitizer_enabled = false;
+
+#if CONFIG_MTE
+bool malloc_has_sec_transition = false;
+uint32_t malloc_sec_transition_policy = 0;
+#endif
+
+#if __LIBLIBC_F_ASAN_INSTRUMENTATION
+static struct malloc_sanitizer_poison malloc_poison_default = {
+ .heap_allocate_poison = __asan_poison_heap_memory_alloc,
+ .heap_deallocate_poison = __asan_poison_heap_memory_free,
+ .heap_internal_poison = __asan_poison_heap_memory_internal,
+};
+static struct malloc_sanitizer_poison *malloc_poison = &malloc_poison_default;
+#else
+static struct malloc_sanitizer_poison *malloc_poison = NULL;
+#endif // __LIBLIBC_F_ASAN_INSTRUMENTATION
+
+MALLOC_NOEXPORT
+malloc_zero_policy_t malloc_zero_policy = MALLOC_ZERO_POLICY_DEFAULT;
+
+static inline malloc_zone_t *
+_find_registered_zone(const void * __unsafe_indexable ptr, size_t *returned_size,
+ bool known_non_default)
+{
+ malloc_zone_t *zone;
+ size_t size;
+
+ // We assume that the initial zones will never be unregistered concurrently
+ // while this code is running so we can have a fast path without
+ // synchronization. Callers who really do unregister these (to install
+ // their own default zone) need to ensure they establish their zone setup
+ // during initialization and before entering a multi-threaded environment.
+ for (uint32_t i = known_non_default ? 1 : 0; i < malloc_num_zones; i++) {
+ zone = _malloc_zones[i];
+ size = zone->size(zone, ptr);
+
+ if (size) { // Claimed by this zone?
+ if (returned_size) {
+ *returned_size = size;
+ }
+
+ return zone;
+ }
+ }
+
+ // Unclaimed by any zone.
+ zone = NULL;
+ size = 0;
+
+ if (returned_size) {
+ *returned_size = size;
+ }
+ return zone;
+}
+
+malloc_zone_t *
+find_registered_zone(const void * __unsafe_indexable ptr, size_t *returned_size,
+ bool known_non_default)
+{
+ return _find_registered_zone(ptr, returned_size, known_non_default);
+}
+
+/********* Creation and destruction ************/
+
+static void
+_malloc_zone_register(malloc_zone_t *zone, bool make_default)
+{
+ /* scan the list of zones, to see if this zone is already registered. If
+ * so, print an error message and return. */
+ for (unsigned i = 0; i < malloc_num_zones; ++i) {
+ if (zone == _malloc_zones[i]) {
+ malloc_report(MALLOC_REPORT_CRASH,
+ "Attempted to register duplicate zone: %p\n", zone);
+ return;
+ }
+ }
+
+ /* maximum number of zones has been reached */
+ if (malloc_num_zones == MAX_MALLOC_ZONES) {
+ malloc_report(MALLOC_REPORT_CRASH, "No capacity for zone: %p\n", zone);
+ return;
+ }
+
+ /* unsupported zone version */
+ if (zone->version < 13) {
+ malloc_report(MALLOC_REPORT_CRASH, "Unsupported zone version: %u\n",
+ zone->version);
+ return;
+ }
+
+ if (make_default) {
+ memmove(&_malloc_zones[1], &_malloc_zones[0],
+ malloc_num_zones * sizeof(malloc_zone_t *));
+ _malloc_zones[0] = zone;
+ } else {
+ _malloc_zones[malloc_num_zones] = zone;
+ }
+
+ malloc_report(ASL_LEVEL_INFO, "Registered zone %p at index %u\n", zone,
+ malloc_num_zones);
+
+ ++malloc_num_zones;
+}
+
+void __malloc_init(const char * __null_terminated * __null_terminated args)
+{
+ logical_ncpus = _liblibc_plat_num_cpus;
+ phys_ncpus = _liblibc_plat_num_cpus;
+
+#if CONFIG_MTE
+ malloc_has_sec_transition = xrt__has_sec_transition();
+#endif
+
+ const unsigned malloc_debug_flags = MALLOC_ABORT_ON_CORRUPTION |
+ MALLOC_ABORT_ON_ERROR;
+ malloc_zone_t *xzone = xzm_main_malloc_zone_create(malloc_debug_flags,
+ NULL, args, NULL);
+ _malloc_zone_register(xzone, true);
+ malloc_set_zone_name(xzone, DEFAULT_MALLOC_ZONE_STRING);
+
+#if __LIBLIBC_F_ASAN_INSTRUMENTATION
+ if ((malloc_sanitizer_enabled = sanitizer_should_enable())) {
+ malloc_zone_t *sanitizer = sanitizer_create_zone(xzone);
+ _malloc_zone_register(sanitizer, true);
+ malloc_set_zone_name(sanitizer, DEFAULT_SANITIZER_ZONE_STRING);
+ }
+#endif // __LIBLIBC_F_ASAN_INSTRUMENTATION
+}
+
+malloc_zone_t* malloc_default_zone(void)
+{
+ return _malloc_zones[0];
+};
+
+/********* Block creation and manipulation ************/
+
+void * __sized_by_or_null(size)
+_malloc_zone_malloc(malloc_zone_t *zone, size_t size, malloc_zone_options_t mzo)
+{
+ // This and similar conditionals are commented out to avoid compiler
+ // warnings on unreachable code
+ // if (os_unlikely(malloc_too_large(size))) {
+ // malloc_set_errno_fast(mzo, ENOMEM);
+ // return NULL;
+ // }
+
+ // zone versions >= 13 set errno on failure so we can tail-call
+ return zone->malloc(zone, size);
+}
+
+MALLOC_NOINLINE
+void * __sized_by_or_null(size)
+malloc_zone_malloc(malloc_zone_t *zone, size_t size)
+{
+ return _malloc_zone_malloc(zone, size, MZ_NONE);
+}
+
+void * __sized_by_or_null(num_items * size)
+_malloc_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size,
+ malloc_zone_options_t mzo)
+{
+ size_t total_bytes;
+ if (calloc_get_size(num_items, size, 0, &total_bytes)) {
+ malloc_set_errno_fast(mzo, ENOMEM);
+ return NULL;
+ }
+
+ // zone versions >= 13 set errno on failure so we can tail-call
+ return __unsafe_forge_bidi_indexable(void *,
+ zone->calloc(zone, num_items, size), total_bytes);
+}
+
+MALLOC_NOINLINE
+void * __sized_by_or_null(num_items * size)
+malloc_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size)
+{
+ return _malloc_zone_calloc(zone, num_items, size, MZ_NONE);
+}
+
+void * __sized_by_or_null(size)
+_malloc_zone_valloc(malloc_zone_t *zone, size_t size, malloc_zone_options_t mzo)
+{
+ // if (os_unlikely(malloc_too_large(size))) {
+ // malloc_set_errno_fast(MZ_NONE, ENOMEM);
+ // return NULL;
+ // }
+
+ void *ptr = zone->valloc(zone, size);
+ if (os_unlikely(ptr == NULL)) {
+ malloc_set_errno_fast(mzo, ENOMEM);
+ }
+
+ return ptr;
+}
+
+MALLOC_NOINLINE
+void * __sized_by_or_null(size)
+malloc_zone_valloc(malloc_zone_t *zone, size_t size)
+{
+ return _malloc_zone_valloc(zone, size, MZ_NONE);
+}
+
+void * __sized_by_or_null(size)
+_malloc_zone_realloc(malloc_zone_t *zone, void * __unsafe_indexable ptr,
+ size_t size, malloc_type_descriptor_t type_desc)
+{
+ // if (os_unlikely(malloc_too_large(size))) {
+ // return NULL;
+ // }
+
+ return zone->realloc(zone, ptr, size);
+}
+
+MALLOC_NOINLINE
+void * __sized_by_or_null(size)
+malloc_zone_realloc(malloc_zone_t *zone, void * __unsafe_indexable ptr,
+ size_t size)
+{
+ return _malloc_zone_realloc(zone, ptr, size, MALLOC_TYPE_DESCRIPTOR_NONE);
+}
+
+MALLOC_NOINLINE
+void
+malloc_zone_free(malloc_zone_t *zone, void * __unsafe_indexable ptr)
+{
+ zone->free(zone, ptr);
+}
+
+static void
+malloc_zone_free_definite_size(malloc_zone_t *zone, void * __sized_by(size) ptr,
+ size_t size)
+{
+ zone->free_definite_size(zone, ptr, size);
+}
+
+malloc_zone_t *
+malloc_zone_from_ptr(const void * __unsafe_indexable ptr)
+{
+ if (!ptr) {
+ return NULL;
+ } else {
+ return _find_registered_zone(ptr, NULL, false);
+ }
+}
+
+void * __alloc_align(2) __alloc_size(3) __sized_by_or_null(size)
+_malloc_zone_memalign(malloc_zone_t *zone, size_t alignment, size_t size,
+ malloc_zone_options_t mzo, malloc_type_descriptor_t type_desc)
+{
+ void * __bidi_indexable ptr = NULL;
+ int err = ENOMEM;
+
+ // if (os_unlikely(malloc_too_large(size))) {
+ // goto out;
+ // }
+
+ // excludes 0 == alignment
+ // relies on sizeof(void *) being a power of two.
+ if (alignment < MALLOC_ZONE_MALLOC_DEFAULT_ALIGN ||
+ 0 != (alignment & (alignment - 1))) {
+ err = EINVAL;
+ goto out;
+ }
+ // C11 aligned_alloc requires size to be a multiple of alignment, but
+ // posix_memalign does not
+ if ((mzo & MZ_C11) && (size & (alignment - 1)) != 0) {
+ err = EINVAL;
+ goto out;
+ }
+
+ if (!(zone->memalign)) {
+ goto out;
+ }
+ ptr = zone->memalign(zone, alignment, size);
+
+out:
+ if (os_unlikely(ptr == NULL)) {
+ if (mzo & MZ_POSIX) {
+ malloc_set_errno_fast(mzo, err);
+ }
+ }
+ return ptr;
+}
+
+MALLOC_NOINLINE
+void * __sized_by_or_null(size)
+malloc_zone_memalign(malloc_zone_t *zone, size_t alignment, size_t size)
+{
+ return _malloc_zone_memalign(zone, alignment, size, MZ_NONE,
+ MALLOC_TYPE_DESCRIPTOR_NONE);
+}
+
+MALLOC_NOINLINE
+void * __sized_by_or_null(size)
+malloc_zone_malloc_with_options(malloc_zone_t *zone, size_t align,
+ size_t size, malloc_zone_malloc_options_t options)
+{
+ if (align != MALLOC_ZONE_MALLOC_DEFAULT_ALIGN &&
+ (os_unlikely((align != 0) && (!powerof2(align) ||
+ ((size & (align-1)) != 0))))) { // equivalent to (size % align != 0)
+ return NULL;
+ }
+
+ if (zone == NULL) {
+ zone = malloc_zones[0];
+ }
+
+ if (zone->version >= 15 && zone->malloc_with_options) {
+ return zone->malloc_with_options(zone, align, size, options);
+ }
+
+ if (align > MALLOC_ZONE_MALLOC_DEFAULT_ALIGN) {
+ void *ptr = zone->memalign(zone, align, size);
+ if (ptr && (options & MALLOC_ZONE_MALLOC_OPTION_CLEAR)) {
+ memset(ptr, 0, size);
+ }
+ return ptr;
+ } else if (options & MALLOC_ZONE_MALLOC_OPTION_CLEAR) {
+ return zone->calloc(zone, 1, size);
+ } else {
+ return zone->malloc(zone, size);
+ }
+}
+
+MALLOC_NOINLINE
+void * __sized_by_or_null(size)
+malloc_zone_malloc_with_options_np(malloc_zone_t *zone, size_t align,
+ size_t size, malloc_options_np_t options)
+{
+ return malloc_zone_malloc_with_options(zone, align, size, options);
+}
+
+boolean_t
+malloc_zone_claimed_address(malloc_zone_t *zone, void *ptr)
+{
+ if (!ptr) {
+ // NULL is not a member of any zone.
+ return false;
+ }
+
+ if (!zone->claimed_address) {
+ // For zones that have not implemented claimed_address, we always have
+ // to return true to avoid a false negative.
+ return true;
+ }
+
+ return zone->claimed_address(zone, ptr);
+}
+
+/********* Functions for zone implementors ************/
+
+void
+malloc_set_zone_name(malloc_zone_t *z, const char *name)
+{
+ if (z->zone_name) {
+ malloc_zone_t *old_zone = _find_registered_zone(z->zone_name, NULL,
+ false);
+ if (old_zone) {
+ malloc_zone_free(old_zone, (char *)z->zone_name);
+ }
+ z->zone_name = NULL;
+ }
+
+ if (name) {
+ const size_t buflen = strlen(name) + 1;
+ char * __sized_by(buflen) name_copy =
+ __unsafe_forge_bidi_indexable(char *, malloc_zone_malloc(z, buflen),
+ buflen);
+ if (name_copy) {
+ strlcpy(name_copy, name, buflen);
+ z->zone_name = __unsafe_null_terminated_from_indexable(name_copy,
+ name_copy + buflen - 1);
+ }
+ }
+}
+
+const char *
+malloc_get_zone_name(malloc_zone_t *zone)
+{
+ return zone->zone_name;
+}
+
+void
+find_zone_and_free(void * __unsafe_indexable ptr, bool known_non_default)
+{
+ malloc_zone_t *zone;
+ size_t size;
+ if (!ptr) {
+ return;
+ }
+
+ zone = _find_registered_zone(ptr, &size, known_non_default);
+ if (!zone) {
+ malloc_report_pointer_was_not_allocated(MALLOC_REPORT_CRASH, ptr);
+ } else if (zone->free_definite_size) {
+ malloc_zone_free_definite_size(zone,
+ __unsafe_forge_bidi_indexable(void *, ptr, size), size);
+ } else {
+ malloc_zone_free(zone, ptr);
+ }
+}
+
+/********* Generic ANSI callouts ************/
+
+void * __sized_by_or_null(size)
+malloc(size_t size)
+{
+ return malloc_zone_malloc(_malloc_zones[0], size);
+}
+
+void * __sized_by_or_null(size)
+aligned_alloc(size_t alignment, size_t size)
+{
+ return _malloc_zone_memalign(_malloc_zones[0], alignment, size,
+ MZ_POSIX | MZ_C11, MALLOC_TYPE_DESCRIPTOR_NONE);
+}
+
+void * __sized_by_or_null(num_items * size)
+calloc(size_t num_items, size_t size)
+{
+ return malloc_zone_calloc(_malloc_zones[0], num_items, size);
+}
+
+void
+_free(void * __unsafe_indexable ptr)
+{
+ if (!ptr) {
+ return;
+ }
+
+ malloc_zone_t * __single zone0 = _malloc_zones[0];
+ if (zone0->try_free_default) {
+ zone0->try_free_default(zone0, ptr);
+ } else {
+ find_zone_and_free(ptr, false);
+ }
+}
+
+void
+free(void * __unsafe_indexable ptr)
+{
+ return _free(ptr);
+}
+
+void * __sized_by_or_null(new_size)
+_realloc(void * __unsafe_indexable in_ptr, size_t new_size)
+{
+ void * __bidi_indexable retval = NULL;
+ void * __unsafe_indexable old_ptr;
+ malloc_zone_t *zone;
+
+ // SUSv3: "If size is 0 and ptr is not a null pointer, the object
+ // pointed to is freed. If the space cannot be allocated, the object
+ // shall remain unchanged." Also "If size is 0, either a null pointer
+ // or a unique pointer that can be successfully passed to free() shall
+ // be returned." We choose to allocate a minimum size object by calling
+ // malloc_zone_malloc with zero size, which matches "If ptr is a null
+ // pointer, realloc() shall be equivalent to malloc() for the specified
+ // size." So we only free the original memory if the allocation succeeds.
+ old_ptr = (new_size == 0) ? NULL : in_ptr;
+ if (!old_ptr) {
+ retval = malloc_zone_malloc(_malloc_zones[0], new_size);
+ } else {
+ zone = _find_registered_zone(old_ptr, NULL, false);
+ if (!zone) {
+ malloc_report_pointer_was_not_allocated(MALLOC_REPORT_CRASH,
+ in_ptr);
+ } else {
+ retval = malloc_zone_realloc(zone, old_ptr, new_size);
+ }
+ }
+
+ if (retval == NULL) {
+ malloc_set_errno_fast(MZ_POSIX, ENOMEM);
+ } else if (new_size == 0) {
+ free(in_ptr);
+ }
+ return retval;
+}
+
+void * __sized_by_or_null(new_size)
+realloc(void * __unsafe_indexable in_ptr, size_t new_size)
+{
+ return _realloc(in_ptr, new_size);
+}
+
+void * __sized_by_or_null(new_size)
+reallocf(void * __unsafe_indexable in_ptr, size_t new_size)
+{
+ void *ptr = realloc(in_ptr, new_size);
+
+ if (!ptr && in_ptr && new_size != 0) {
+ // Save and restore `errno`, because `realloc` will set it to ENOMEM
+ // on allocation failure, but it could be overwritten if `free` calls
+ // into a library function that also modifies `errno`
+ errno_t error = errno;
+ free(in_ptr);
+ errno = error;
+ }
+
+ return ptr;
+}
+
+void * __sized_by_or_null(size)
+valloc(size_t size)
+{
+ return _malloc_zone_valloc(_malloc_zones[0], size, MZ_POSIX);
+}
+
+size_t
+malloc_size(const void * __unsafe_indexable ptr)
+{
+ size_t size = 0;
+
+ if (!ptr) {
+ return size;
+ }
+
+ (void)_find_registered_zone(ptr, &size, false);
+ return size;
+}
+
+size_t
+malloc_good_size(size_t size)
+{
+ malloc_zone_t * __single zone = _malloc_zones[0];
+ return zone->introspect->good_size(zone, size);
+}
+
+int
+_posix_memalign(void * __unsafe_indexable *memptr, size_t alignment,
+ size_t size)
+{
+ void * __bidi_indexable retval;
+
+ /* POSIX is silent on NULL == memptr !?! */
+
+ retval = malloc_zone_memalign(_malloc_zones[0], alignment, size);
+ if (retval == NULL) {
+ // To avoid testing the alignment constraints redundantly, we'll rely on
+ // the test made in malloc_zone_memalign to vet each request. Only if
+ // that test fails and returns NULL, do we arrive here to detect the
+ // bogus alignment and give the required EINVAL return.
+ if (alignment < MALLOC_ZONE_MALLOC_DEFAULT_ALIGN || // excludes 0 == alignment
+ 0 != (alignment & (alignment - 1))) { // relies on sizeof(void *)
+ // being a power of two.
+ return EINVAL;
+ }
+ return ENOMEM;
+ } else {
+ *memptr = retval; // Set iff allocation succeeded
+ return 0;
+ }
+}
+
+int
+posix_memalign(void * __unsafe_indexable *memptr, size_t alignment, size_t size)
+{
+ return _posix_memalign(memptr, alignment, size);
+}
+
+boolean_t
+malloc_claimed_address(void *ptr)
+{
+ // We need to check with each registered zone whether it claims "ptr".
+ // Use logic similar to that in find_registered_zone().
+ if (malloc_num_zones == 0) {
+ return false;
+ }
+
+ // Next, try the initial zones.
+ for (uint32_t i = 0; i < malloc_num_zones; i++) {
+ if (malloc_zone_claimed_address(_malloc_zones[i], ptr)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void * __sized_by_or_null(nmemb * size)
+reallocarray(void * in_ptr, size_t nmemb, size_t size)
+{
+ size_t alloc_size;
+ if (os_mul_overflow(nmemb, size, &alloc_size)){
+ malloc_set_errno_fast(MZ_POSIX, ENOMEM);
+ return NULL;
+ }
+ return realloc(in_ptr, alloc_size);
+}
+
+void * __sized_by_or_null(nmemb * size)
+reallocarrayf(void * in_ptr, size_t nmemb, size_t size)
+{
+ size_t alloc_size;
+ if (os_mul_overflow(nmemb, size, &alloc_size)){
+ malloc_set_errno_fast(MZ_POSIX, ENOMEM);
+ return NULL;
+ }
+ return reallocf(in_ptr, alloc_size);
+}
+
+/********* Functions for sanitization ************/
+
+bool malloc_sanitizer_is_enabled(void)
+{
+ return malloc_sanitizer_enabled;
+}
+
+const struct malloc_sanitizer_poison *
+malloc_sanitizer_get_functions(void)
+{
+ return malloc_poison;
+}
+
+void
+malloc_sanitizer_set_functions(struct malloc_sanitizer_poison *s)
+{
+ malloc_poison = s;
+}
+
+/********* Debug helpers ************/
+
+void
+malloc_zone_print_ptr_info(void * __unsafe_indexable ptr)
+{
+ malloc_zone_t *zone;
+ if (!ptr) {
+ return;
+ }
+ zone = malloc_zone_from_ptr(ptr);
+ if (zone) {
+ printf("ptr %p in registered zone %p\n", ptr, zone);
+ } else {
+ printf("ptr %p not in heap\n", ptr);
+ }
+}
+
+boolean_t
+malloc_zone_check(malloc_zone_t *zone)
+{
+ boolean_t ok = 1;
+ if (!zone) {
+ unsigned index = 0;
+ while (index < malloc_num_zones) {
+ zone = _malloc_zones[index++];
+ if (!zone->introspect->check(zone)) {
+ ok = 0;
+ }
+ }
+ } else {
+ ok = zone->introspect->check(zone);
+ }
+ return ok;
+}
+
+void
+malloc_zone_print(malloc_zone_t *zone, boolean_t verbose)
+{
+ if (!zone) {
+ unsigned index = 0;
+ while (index < malloc_num_zones) {
+ zone = _malloc_zones[index++];
+ zone->introspect->print(zone, verbose);
+ }
+ } else {
+ zone->introspect->print(zone, verbose);
+ }
+}
+
+/********* Misc other entry points ************/
+
+void
+malloc_zero_on_free_disable(void)
+{
+ malloc_zone_error(MALLOC_ABORT_ON_ERROR, false,
+ "xzone cannot disable zero on free");
+}