Darwin: libsystem_malloc .dylib and XZone
DFFIND
DFF Labs
Resources
Careers<br>About
Resources
Careers<br>About<br>Contact us
Contact us
Darwin: libsystem_malloc .dylib and XZone
Darwin’s new allocator, XZone, is now part of libmalloc and in open source. In this post, we are providing an analysis of XZone that is an expanded version of an excerpt from Jonathan Levin’s book “Disarming Code”.
PUBLISHED ON<br>June 8, 2026
AUTHOR<br>Jonathan Levin
Share
-->
-->
Table of Contents<br>Example H2<br>Example H3
Darwin’s new allocator, XZone, is now part of libmalloc and has been open sourced. In this post, we are providing an analysis of XZone that is an expanded version of an excerpt from Jonathan Levin’s book “Disarming Code”. The book is available at https://NewDebuggingBook.com/<br>Darwin: libsystem_malloc.dylib and XZone<br>Apple introduced a new allocator called XZone malloc as far back as 2023, but kept its sources out of the libmalloc distribution, likely due to its support for MTE, which was not acknowledged until Darwin 25 and the A19/M5 chipsets. Early reverse engineering attempts include Dataflow Security's blog. This changed with libmalloc-792, which not only open-sources the allocator, but also includes documentation.<br>XZone malloc is derived from Microsoft's mimalloc, and the 'X' implies "XNU-Style", similar to the kernel's own Zone allocator, which is discussed later in 13/2.2.2. There are parallels in several key features, including type isolation, separate metadata regions for allocations, probabilistic guard pages.<br>Prelude: libsystem_malloc<br>Darwin's libsystem_malloc.dylib (reexported by libSystem.B.dylib) provides the platform's heap implementation. A detailed discussion of the library (up to Darwin 19) can be found in M-I/9-15. The library uses malloc "zones" - malloc_zone_t structures - allowing multiple providers with a modular implementation of the user-mode memory management functions. The structure has continuously evolved over the years, and the following figure "catches up" with Darwin 25 (structure version 16, libmalloc version 792):
Figure 6/2-43: The libmalloc malloc_zone_t (libmalloc 792, D25) (cf. M-I/9-9)Typed Memory Operations<br>Darwin 23 (libmalloc 521, structure version 16) added malloc_type_* variants. The interface (in malloc_type_* variants of [m/c/re]alloc, memalign and malloc_with_options, which take another argument of malloc_type_id_t - a 64-bit, shown in Listing 6/2-44 (next page), adapted from a comment in libmalloc-792's sources.
Listing 6/2-44: The malloc_type_id_t (from libmalloc-792)The typed allocations are contingent on the compiler's support, which can be detected using __has_feature(typed_memory_operations). As speculated in the initial, pre-Darwin 25 edition of this book, this indeed hinted at the adoption of ARMv8.5 MTE (and the XZone malloc allocator, discussed in this later edition).<br>Terminology<br>XZM divides the heap into mach_vm_allocate()d ranges known as segments . These are presently #defined to be XZM_SEGMENT_SIZE (= 4MB) in size. The smallest unit in a segment is a slice (XZM_SEGMENT_SLICE_SIZE = 16KB, per the page size). Contiguous slices can be grouped into a span . The documentation refers to a span with one or more blocks as a chunk . Chunks can hold up to 2 or 4 blocks, depending on the allocation strategy: Tiny blocks are up to 4KB in a 16KB chunk, Small blocks are up to 32KB in a 64KB chunk, Large chunks go up to 2MB blocks (and one block per chunk), and Huge ones span the entire segment.<br>XZM performs tiny and small allocations from xzones .* As with other slab allocators, each XZone serves an allocation of some given bin size . There are 40 zone bins, ranging in size from 16 bytes to 32K (preinitialized from _xzm_bin_sizes).<br>Each bin further supports 4 (iOS26) or 6 (macOS.2) buckets , which separate similar sized allocations, so they don't end up next to one another (similar to XNU's kalloc.type.varnn.size). The aforementioned malloc_type_descriptor_t is used to determine the bucket type (the documentation refers to this as "Type Isolation"). Zone '0' is unused (indexing starts at XZM_XZONE_INDEX_FIRST =1), so this yields 161 (iOS) or 241 (macOS) zones.<br>The xzm_main_zone structure<br>XZM interfaces with libmalloc through the malloc_zone_t structure (shown in Figure 2-43), which links its function implementations to the well known memory management APIs. The first zone created is an xzm_main_malloc_zone_s, which can be thought of as a subclass of an xzm_malloc_zone_s, itself a subclass of malloc_zone_t. Additional xzm_malloc_zone_ss may be created, in which case their xzz_main_ref field will hold the reference to the main zone.<br>The main zone holds the global allocation data for the entire process. There are dozens of fields in this combined structure, and Figure 6/2-45 attempts to highlight some of the important ones:
Figure 6/2-45: The xzm_main_alloc_zone_s, and some important fieldsSegments Table(s) and Segment Groups<br>The Segment Table manages 64GB of virtual...