[libpas] Use tagged_bmalloc_heap for MTE allocations
Component: libpas / bmalloc allocator | c8cb35b
Source/bmalloc/bmalloc/bmalloc.cpp
Source/bmalloc/bmalloc/TZoneHeap.cpp
libpas is the allocator behind bmalloc, FastMalloc and TZoneMalloc. Until now a single pas_heap had to serve both MTE-tagged and untagged allocations, and "compact" allocations (pointers that fit a compressed 32-bit form) needed their own megapages and heap-refs. With ARM MTE, memory carries a 4-bit tag that is checked on access and re-randomized on free to catch use-after-free and overflows. Because every heap could tag, tag-setting code was inlined everywhere and heaps had to mmap different memory flavors.
This commit enables the previously compiled-in but unused tagged_bmalloc_heap for MTE-tagged FastMalloc allocations. Compactness is hoisted out of libpas: pas_allocation_mode is deleted and compact/non-compact megapages and heap-refs are collapsed. bmalloc.h dispatches between the tagged and default heaps. The free path speculates non-MTE first, then checks MTE, then falls through to the casual case. Tag-setting moves out of line behind the tagged heap, and the tagging decision moves from per-allocator is_mte_tagged to the heap config. Sufficiently large compact objects can now be delegated to libmalloc, and the small_medium_bootstrap heap files are renamed pas_mte_bootstrap_*.
Allocation (bmalloc::api::malloc, mode = Compact | NonCompact):
malloc(size, mode)
├─► Compact ──► bmalloc_heap
└─► NonCompact
├─► MTE enabled (hardened processes), taggable size
│ └─► tagged_bmalloc_heap (tag-set / retag out of line)
└─► MTE disabled, or above tagging ceiling
└─► bmalloc_heap (no tag code inlined; WebContent fast path)
Free (bmalloc::api::free):
Before: bmalloc_deallocate_inline (one heap)
After: 1. inline-only bmalloc_heap free (small-segregated, TLC present, non-MTE) -- unchanged fast path
else freeOutOfLine:
2. bmalloc_try_deallocate_casual
3. tagged_bmalloc_try_deallocate_inline_only
4. tagged_bmalloc_deallocate_casual
Significance
In MTE-enabled processes, non-compact allocations now come from a separate tagged heap, which changes how they are tagged, freed and laid out, and with them the heap-grooming and MTE-bypass assumptions built on that layout. The default heap's inline fastMalloc shrinks from 141 to 73 instructions because tag-setting moves out of line, while fastFree grows from 44 to 45. The non-MTE WebContent fast path is deliberately left essentially untouched.
Audit directions
This is a very large allocator rewrite touching the core allocate, free, realloc and large-heap paths, and it changes invariants that exploit mitigations rely on.
- Free-path dispatch. The new order is speculative non-MTE, then MTE, then casual. Look for pointers misrouted to the wrong heap, double-free or type confusion across the tagged and default heaps, and the
isMTEEnabledUnsafefast check being raced or stale. pas_large_heap. The commit says entries must not be removed unless the heap owns them, because a failed try-deallocate otherwise leaves later frees unable to find the entry. Review the newtry_deallocateand delegation paths, and thelarge_object_delegation_is_enabledandshould_delegate_user_allocation_to_system_malloclogic, for lifetime or metadata corruption.- libmalloc delegation. Large compact objects are now eligible. Check how ownership,
owns_objectand realloc behave between libpas and libmalloc, and what this does to heap-grooming and layout predictability. - Tagging decisions from the heap config. Check
pas_mte_maybe_tag_allocated_region,pas_mte_retag_freed_region_if_taggedand the adjacent-tag exclusion logic for objects that end up untagged or double-tagged, especially JSC Structure blocks, which use alignment-sensitive auxiliary allocations. - Compact vs. non-compact removal. Collapsing the megapages changes address-range assumptions. Check compact pointer encoding, enumeration (
testEnumerationInvalidCompactHeapBumpwas touched) and the reallocate copy paths. The allocation-mode removal across many signatures is mechanical, so look for flex, iso, PGM and bitfit call sites that now take a different default.