[libpas] Guard page at the front of the compact-heap reservation
Component: bmalloc libpas | ba26b52
Source/bmalloc/libpas/src/libpas/pas_compact_heap_reservation.c
Source/bmalloc/libpas/src/libpas/pas_page_malloc.c
libpas packs allocator metadata into a fixed VA reservation and references those objects by 8-byte-aligned index rather than by full pointer, to shrink per-object overhead. Indices 0 and 1 are never handed out — the first byte of compact memory is reached via index 2 — but before this change that reservation was purely arithmetic: the base was computed as the mapped page minus guard_size, so decoding index 0 pointed outside the reservation, into an adjacent ordinarily-mapped VM region. The patch mprotects the first page PROT_NONE and sets the base to the mapped address, making the guard real.
Before: After:
compact idx 0 -> base-guard_size (mapped) compact idx 0 -> base+0 (PROT_NONE)
corrupts neighbor VM SIGSEGV, deterministic
reservation_base = page - guard_size reservation_base = page
available_size = size - guard_size available_size removed; size unchanged
Significance
A zeroed compact pointer decoded through an unchecked _load_non_null accessor now faults deterministically instead of silently corrupting whatever is mapped next door. That converts a class of compact-pointer-corruption bugs from an exploitable primitive into a debuggable crash.
Audit directions
This is a mitigation for bugs elsewhere, not new surface — but it doubles as a map of what the vendor considers exploitable: any code path that can zero or corrupt a compact pointer's index before it is dereferenced through an unchecked _load_non_null-style accessor. Narrow: check whether a single page of guard_size is large enough to catch multi-index-off corruption, not just the zero case. Wider: audit the remaining call sites that still reach pas_page_malloc_try_allocate_without_deallocating_padding directly and therefore bypass the guard entirely — the PLAYSTATION branch is left unguarded — and, more portably, enumerate every other index-encoded pointer space in libpas and ask whether its index-zero decode lands on mapped memory. Match tell: a reservation whose base is computed by subtracting a guard size rather than by mapping one.