# hardened_malloc in UniversalisOS — Config Matrix, Page-Size Contract & Integration **Track:** T8 (hardened_malloc Import & Separation-Model Adoption) **Source:** `universalisos/third_party/hardened_malloc/` (see `PROVENANCE.md`) **Status:** T8-1.1 (vendored) ✅ · T8-1.3 host build + `make test` 51/51 ✅ · T8-1.2 (musl personality) greenfield — see §4 > hardened_malloc is the designated **stepping stone to running multiple concurrent hardened > Android/AOSP/LineageOS/GrapheneOS guests** inside UniversalisOS. It is the only GrapheneOS > component portable across the guest/host boundary (Bionic/musl/glibc). Master plan §0.14 + TRACK T8. --- ## 1. What was built and verified (2026-07-11) | Artifact | Result | |---|---| | `out/libhardened_malloc.so` (VARIANT=default) | **Built clean** — 53 KB, C23, `-O3 -flto -fPIC`, all hardening flags active | | `make test` (acceptance gate) | **51/51 OK** (double-free, overflow, quarantine, canary, usable-size, object-size, realloc/calloc edge cases) | | Exported malloc API surface | `malloc calloc realloc free aligned_alloc posix_memalign memalign cfree reallocarray` + hardened extensions `malloc_object_size{,_fast} free_sized free_aligned_sized malloc_usable_size malloc_stats malloc_info malloc_trim` | | Live LD_PRELOAD smoke test | **PASS** — real program allocates/uses/frees correctly under `LD_PRELOAD=out/libhardened_malloc.so` | Build host: GCC 16.1.1 / Clang 22.1.8 (both C23-capable). `CONFIG_NATIVE=true` → `-march=native`. --- ## 2. Config matrix — `default` vs `light` (from `config/{default,light}.mk`) | Flag | `default` | `light` | Meaning | |---|---|---|---| | `CONFIG_ZERO_ON_FREE` | true | true | Clear memory on free (anti data-leak) | | `CONFIG_WRITE_AFTER_FREE_CHECK` | **true** | false | Detect write-after-free via canary | | `CONFIG_SLOT_RANDOMIZE` | **true** | false | Randomize slot allocation order | | `CONFIG_SLAB_CANARY` | true | true | Per-allocation canary | | `CONFIG_SLAB_QUARANTINE_RANDOM_LENGTH` | 1 | 0 | Random quarantine (use-after-free detection) | | `CONFIG_SLAB_QUARANTINE_QUEUE_LENGTH` | 1 | 0 | FIFO quarantine | | `CONFIG_GUARD_SLABS_INTERVAL` | 1 | 8 | How often slabs get guard pages | | `CONFIG_EXTENDED_SIZE_CLASSES` | true | true | Finer-grained size classes | | `CONFIG_LARGE_SIZE_CLASSES` | true | true | Large-allocation size classes | | `CONFIG_REGION_QUARANTINE_RANDOM_LENGTH` | 256 | 256 | Large-region quarantine | | `CONFIG_REGION_QUARANTINE_QUEUE_LENGTH` | 1024 | 1024 | Large-region FIFO quarantine | | `CONFIG_CLASS_REGION_SIZE` | 32 GiB | 32 GiB | Per-size-class virtual region | | `CONFIG_N_ARENA` | 4 | 4 | Independent arenas (scalability/isolation) | | `CONFIG_SELF_INIT` | true | true | Self-initializing via `constructor(101)` (correct for musl/standalone) | | `CONFIG_STATS` | false | false | Runtime stats (enable for debugging) | **Selection guidance (T8-1.4):** - **musl POSIX personality (primary target):** `default` — full hardening; the personality is a controlled, trusted environment where the overhead is acceptable and the isolation value is highest. - **Constrained / RTOS-class guests:** `light` — drops write-after-free check, slot randomization, and slab quarantine for lower overhead while keeping zero-on-free + canary + region quarantine. - **Host dev tools (opportunistic, glibc):** `default` via `preload.sh` LD_PRELOAD. - **Android guests:** *inherited* — Bionic ships its own hardened_malloc build (`platform_bionic` `h_malloc_wrapper.cpp`, `Android.bp` with `CONFIG_SELF_INIT=false`, `N_ARENA=1`). UniversalisOS does **not** rebuild it; it provides the Stage-2 MM features (§5). Build a variant: `make VARIANT=default` (or `light`) → `out/libhardened_malloc.so` (or `out-light/libhardened_malloc-light.so`). --- ## 3. Page-size contract (HARD) hardened_malloc assumes a **4 KiB page size**, enforced at **compile time**: ```c // h_malloc.c:326 static_assert(PAGE_SIZE == 4096, "bitmap handling will need adjustment for other page sizes"); ``` (The subagent noted a runtime `sysconf(_SC_PAGESIZE)` check; the binding constraint is actually this compile-time `static_assert` plus the runtime `sysconf` guard.) **Consequence for UniversalisOS:** any personality linking hardened_malloc **must use 4 KiB pages**. - **AArch64:** 4 KiB translation granule is the common case → ✅ compatible. (UOS Stage-2 uses 4 KiB.) - **riscv64 / armv7:** must confirm 4 KiB granule; 16 KiB/64 KiB granule configs are **incompatible** without porting the bitmap handling. - This is a **hard contract** — a personality reporting a non-4 KiB page size will fail to build/boot with hardened_malloc. --- ## 4. T8-1.2 — musl POSIX personality baseline (GREENFIELD) **Current state (verified 2026-07-11):** `universalisos/kernel/src/core/posix/` contains only config stubs (`posix_config.c/h`). There is **no musl/libc userspace personality yet**. The existing guests (`guests/linux-aarch64`, `riscv-sampling`, `wasm3-app`) are full OS guests, not a libc-bearing POSIX personality. So T8-1.2 is genuinely greenfield. **Why musl:** hardened_malloc explicitly prefers musl ("a much more robust and cleaner base", `README.md:38`). A musl-based personality also gives UniversalisOS a clean, non-Google libc story — fits the "freedom from technology" north star. **Path to stand it up (next actions, in order):** 1. **Toolchain:** obtain an `aarch64-linux-musl` cross toolchain (buildroot can emit one — the existing `guests/linux-aarch64/buildroot-*` trees support `BR2_TOOLCHAIN_BUILDROOT_LIBC="musl"`; or use a prebuilt musl-cross). Host currently has only `aarch64-linux-gnu-*` (glibc). 2. **Personality skeleton:** a minimal EL1/EL0 userspace that the UOS kernel loads as a partition (reuse the `guests/` boot path + `partition.h`), with musl as its libc and a `hello-world` that calls `malloc`/`printf`. 3. **Wire hardened_malloc:** link `libhardened_malloc.so` (built for musl/aarch64 via the standalone Makefile, `CONFIG_SELF_INIT=true`) ahead of musl's malloc so it becomes the system allocator. 4. **Gate:** personality boots under UOS, `hello-world` allocates/frees via hardened_malloc, and hardened_malloc's `make test` (cross-built) passes in the personality. **This is the next concrete T8 milestone after the host proof (done).** --- ## 5. `KERNEL_FEATURE_WISHLIST.md` → UniversalisOS personality-MM (T8-2.1) UniversalisOS *is* the kernel, so it can natively provide what hardened_malloc merely *wishes* Linux had: | Wishlist item (`KERNEL_FEATURE_WISHLIST.md`) | UOS opportunity | |---|---| | much higher `vm.max_map_count` | hardened_malloc creates many VMAs (guard + per-class regions); UOS personality MM sets a high count natively | | disable brk heap / mmap grows upwards | from-scratch personality simply doesn't provide brk | | alternative to `RLIMIT_AS` for accountable mappings | map to the partition memory domain (`partition.h` memory partitioning) | | `MREMAP_DONTUNMAP` with expansion | `memory.c:92-110` uses mremap under `HAVE_COMPATIBLE_MREMAP`; UOS implements the exact semantics | | **first-class arbitrarily-sized guard pages** | hardened_malloc *emulates* them with separate `PROT_NONE` VMAs (`pages.c:13-30`); UOS Stage-2/personality MMU provides them natively — halves VMA count + syscalls | | virtual memory quarantine | UOS quarantines freed virtual regions at the personality level | **Design consequence:** treat `KERNEL_FEATURE_WISHLIST.md` as a **requirements document for the UOS personality-MM/syscall layer**. UOS can give hardened_malloc a *better* substrate than Linux — a genuine differentiator and a concrete T8-2 workstream. --- ## 6. Why this is the stepping stone to multi-Android guests (recap) 1. **Only portable component:** hardened_malloc is the sole GrapheneOS component that crosses the guest/host boundary (Bionic/musl/glibc). Importing it into the musl POSIX personality proves the toolchain + CMake integration + musl personality + MM-feature substrate *before* a full Android guest. 2. **Per-partition hardening = the separation model made concrete:** each PikeOS partition gets its own hardened_malloc instance (independent random bases, quarantine queues, guard regions). A heap-corruption exploit in one guest is contained by (a) isolated Stage-2 address spaces and (b) independently-randomized allocator metadata. Two independent layers. 3. **Forces the MM substrate early:** guard pages, high map counts, mremap — exactly what a multi-guest hypervisor needs anyway. Building it for hardened_malloc first de-risks the multi-guest Stage-2 work. --- ## 7. Acceptance status - **T8-1.1** vendor + provenance — ✅ DONE (`third_party/hardened_malloc/`, `PROVENANCE.md`) - **T8-1.3** host build `libhardened_malloc.so` + `make test` 51/51 + LD_PRELOAD smoke — ✅ DONE (host proof) - **T8-1.4** config matrix + page-size contract + this doc — ✅ DONE - **T8-1.2** musl POSIX personality baseline — ⏳ GREENFIELD, next milestone (§4)