Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ADR-0043: Monotonic FFI allocation-owner IDs

Date: 2026-07-26 / Status: Accepted. Superseded only in its version numbers by ADR-0044, which raised the FFI ABI again; the owner-ID ownership rule and the descriptor layouts decided here are unchanged. Current values are fmf-contract::versions, not this text.

FmfPage and FmfBlob carry a nonzero owner_id: u64. Their free exports accept only that ID:

int32_t fmf_page_free(uint64_t owner_id);
int32_t fmf_blob_free(uint64_t owner_id);

Page and blob owners remain in separate live-allocation registries, while their IDs come from one process-wide monotonic namespace. ID zero is the no-allocation/free-no-op sentinel. Unknown, already-freed, forged, stale, and cross-kind IDs return FMF_E_INVALID_ARG without dereferencing caller memory. IDs are never reused; exhaustion fails closed with FMF_E_IO.

The previous address-keyed registries rejected ordinary forged, cross-kind, and double frees, but could not distinguish an old pointer from a newer allocation placed at the same recycled address (ABA). Reading a cookie through the stale pointer would itself be undefined behavior. Returning a generation-bearing owner ID and freeing by ID alone removes foreign addresses from ownership transfer and closes that gap.

This is an incompatible FFI-only POD/signature change, so ABI_VERSION is 4: FmfPage is 40 bytes with owner_id at offset 32, and FmfBlob is 24 bytes with owner_id at offset 16. The named-pipe page/blob encoding, pipe name, and PROTOCOL_VERSION=3 are unchanged. Runtime HelloResp.abi_version naturally reports 4. The shared golden HelloResp is deliberately a literal version-1 representative rather than a snapshot of current constants, so its wire bytes remain unchanged.

Contract tests pin layout/signatures and prove zero no-op, forged/double and cross-kind rejection, and the ABA property: freeing an old ID after allocating a replacement cannot release the replacement.