140 lines
8.8 KiB
Markdown
140 lines
8.8 KiB
Markdown
# 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)
|