Native module loader quarantine
August 28, 2026 · View on GitHub
Audience: maintainers, host integrators, and compiler contributors.
Status: private, workspace-only quarantine. It is used by the unpublished
semaprax-native-host physical ownership stage and its private generated
callable path, but not by ordinary compiler preflight or any public adapter,
and it does not change SPX-B104.
crates/semaprax-native-loader isolates the unavoidable unsafe operations for
opening a trusted native library, resolving one fixed C descriptor getter,
calling it, and reading and comparing an exactly bounded expected byte range.
Its separately versioned callable-v2 constructor additionally resolves one
exact C byte-wire function after descriptor equality. The private settlement-v3
constructor consumes only a structurally bounded SPXNABI3 projection already
accepted by the independent host decoder, resolves its getter plus six-argument
execute and settle entries eagerly, and admits them only when every function
address and the returned descriptor address belong to the canonical root
image, then retains its own immutable copy of the admitted bytes. Unix proves
this with dladdr plus canonical path equality; Windows
uses address-to-module allocation-base resolution plus canonical module-path
equality without adding another image reference. Unix opens use
RTLD_NOW | RTLD_LOCAL, so dependency relocations fail during admission
rather than at a later first call. The main semaprax crate remains
unsafe_code = "forbid". The loader is unpublished. Dynamic-image builds have
one exact-pinned libloading dependency; iOS builds resolve no libloading
dependency and expose only the static settlement registration surface. The
crate exposes no generic symbol lookup, raw handle, raw
pointer, callable pointer, or manual close, and returns only opaque
Arc-backed leases with explicit retention and exact logical-admission
identity. Leases are deliberately neither Send nor Sync, keeping
potential native terminator execution on the opening thread until a future
module contract can prove cross-thread teardown safe.
The static settlement lane has a mandatory macOS type-check gate for five
distinct iOS-family Rust targets: arm64 device, arm64 and x86_64 simulators, and
arm64 and x86_64 Mac Catalyst. It binds one process-lifetime
descriptor/getter/execute/settle address
tuple to a same-thread exact logical instance and feeds the same private host
receipt/ledger/quarantine composition as dynamic v3. Every iOS build excludes
the dynamic leases, path/image provenance code, and all open_* APIs. The
cross-target gate also fails if libloading reappears in any iOS dependency
graph. The same mandatory job is configured to link one exact generated
arm64-Simulator provider with the private host and run its static lease through
authenticated receipt commit at -O0/-O2. Run 31318280135, job
93257002836
proved that runtime path. It remains a standalone
Simulator process—not device execution, Apple app packaging, lifecycle/UI/Swift
integration, general iOS admission, or public admission.
Android retains dynamic-image profile 1. The mandatory API-35 x86_64 Emulator
job compiles the loader and private host for both
x86_64-linux-android and aarch64-linux-android, requires exact
libloading 0.9.0, compiles target-bound Bionic/ELF providers with pinned NDK
r27.2, and inspects both x86_64 and AArch64 ELFs. The runtime half pushes the
x86_64 provider and standalone host runner to a canonical
/data/local/tmp directory and requires dladdr root-image provenance, exact
O0/O2 finalizers, authenticated receipt/ledger transition, and zero measured
Rust allocation across the irreversible interval. Run 31320436726, job
93262427248
is green. It is not an APK, JNI/Kotlin, app lifecycle/UI, arm64 device,
general-corpus, or public admission claim.
The constructor is intentionally unsafe. Loading executes the selected
image's and dependencies' initializers before descriptor validation and may run
termination routines when the last SEMAPRAX loader reference is released. The
caller must already trust the exact artifact, its module directory, dependency
search behavior, getter ABI, immutable returned byte range, and absence of
foreign unwind. For descriptor-only and callable-v2 admission, canonical paths
are diagnostic metadata and descriptor equality proves only that the resolved
getter returned the caller's expected bytes. Settlement-v3 additionally proves
that its getter, execute, settle, and returned descriptor storage share one
root-image allocation and canonical path, and retains an immutable byte copy;
continued immutability of provider-owned storage remains part of the unsafe
caller contract. None of these checks establish file
identity, signature validity, provider compatibility, or code safety.
The unsafe contract therefore also requires the root path, module directory,
and dependency-search namespace to remain non-adversarially stable throughout
the load. This is not a sandbox or a malicious-plugin boundary.
Current executable evidence uses plain C fixtures on Linux and macOS. The descriptor-only lane proves canonical-path and input bounds, exact-byte comparison, missing path/symbol rejection, null rejection, logical-admission separation, explicit lease retention, and release of SEMAPRAX's loader reference after the last lease. The callable foundation separately proves v1 rejection before loading, bounded and distinct getter/callable names, exact v2 bytes, eager unresolved-import failure on Unix, one exact resolved echo callable, preallocated bounded request/response storage, one-shot invocation, and cross-instance prepared-call rejection. The v3 lane separately proves exact descriptor-derived capacity equations, pairwise-distinct names and resolved addresses, dependency-owned execute and descriptor-address rejection, missing entry rejection, five disjoint preallocations, separate one-shot execute and settle stages, cross-instance rejection, explicit retention, and final loader release on the currently observed Unix host. The equivalent v3 Windows dynamic runtime is green in hosted run 31313341303.
The v3 lease retains exactly one platform Library; provenance queries do not
increment the native reference count. It also retains the exact admitted
descriptor bytes under the existing 64 KiB ceiling and exposes only a narrow
byte-equality check, so an independent host parse cannot be substituted with a
different same-capacity descriptor. Its request, recovery frame,
execute-response, decision, and candidate-receipt buffers are all allocated at
their exact authenticated capacities before execute. No allocation, symbol
lookup, generic lookup, or handle access occurs in either provider call. The
loader owns no poison, draining, quarantine, receipt-authentication, ledger, or
physical-finalizer policy; those remain host responsibilities. The v3 surface
is private and does not change public native admission or SPX-B104.
The loader's standalone plain-C fixtures remain separate provenance evidence.
A private joint test additionally compiles generated providers for all 14
authoritative normal scenarios, admits their exact descriptor and three entry
points through this constructor, and executes them through the host receipt
ledger at -O0/-O2. Seven failure/interruption fixtures also cross loader and
host; canonical pre-execute unwind skips execute and transitions directly to
settlement. Fatal allocator/process-crash evidence remains open.
The unpublished native host additionally proves that its real callable-v2
lease is retained by its same-thread authority and every live owner/result
credential, that equal descriptor bytes from separate opens do not establish
instance identity, and that draining rejects new work while existing owners
keep their pins. The compiler now derives deterministic
SPXNABI2 admission metadata, and the host has an
independent strict staged decoder with cross-crate exact acceptance and
every-byte, truncation, and trailing-data rejection. The host connects that
decoder and callable lease to its authority and ledger; real generated O0/O2
providers execute the complete 14-case corpus through safe host calls.
The private SPXNPRF1 settlement proof
is not a loadable descriptor. A dedicated regression requires the callable-v2
constructor to reject its magic during input validation, before attempting to
open any path. The unpublished host may parse proof bytes independently for
consistency evidence, but no loader constructor or symbol surface accepts them.
The standalone loader retains narrow bounded-call fixtures, while the
ownership-host integration exercises generated SEMAPRAX resource code. The
Windows loader excludes current-directory/legacy-PATH dependency search and
admits the root-image directory plus default safe directories. A mandatory
Windows-only fixture now places a same-name malicious dependency in both the
process current directory and legacy PATH: the sibling dependency must win,
and removing that sibling must fail as LibraryOpen rather than falling back
to the malicious image. CI also names the complete generated O0/O2 callable
corpus as an explicit Windows gate. Both passed in run 31257545008, job
93103151756.
The current evidence still does not prove immediate physical unmapping, broader
Windows application-platform completion, iOS device/app lifecycle or general-
corpus execution, Android device execution,
callback/finalizer quiescence, hot reload, fork recovery, signed code admission,
or callable resource safety. Those remain gates before any public native
adapter or SPX-B104 change. Bounded Linux Rust-host ASan evidence is green in
public job 93107277065.
The Linux ASan/UBSan generated-provider job is green for all 14 O0/O2 cases loaded through this quarantine and the Rust host. It did not instrument the Rust host, and unrelated Clippy/GCC failures kept that historical overall workflow run red; the later Windows evidence is linked above.