Safety and verification

Safety gates

Every check that fails the SlopOS build when the kernel breaks one of its safety rules, and how to run each one.

This page lists every gate: a script that fails the build when the source tree, the compiled kernel or a test run breaks a rule. The scripts live in scripts/ in the SlopOS repository, and each one's header comment is the authoritative description. The framekernel explains why the main rules exist.

Running the gates

CommandRuns
just check-framekernel-gatesEvery gate's self-test, then the source, image and toolchain gates below. Needs a prior just build. This recipe is the single list CI calls.
just check-framekernelThe above, then cargo fmt --check, just check-miri and just verify
just build (any kernel build)The kernel image gates, for the variant just built, through scripts/check_kernel_elf_gates.sh
KERNEL_BUILD_GATES=1 just buildAlso the whole-tree source scans and the TCB ratio, from the build
scripts/<gate>.sh --self-testOne gate's self-test

Every gate except check_unsafe_expansion.sh, check_vendor_pin.sh, tcb_ratio.sh, check_test_count.sh and check_return_types.sh has a --self-test mode, which check-framekernel-gates runs first. Source scanners must reject planted violations with exact hit counts and accept the forms they allow; image gates build small objects with llc (an oversized stack frame, a movups, an undeclared linker section) and must reject them. A failing self-test fails the build.

Allowlist files for gates that read the kernel image or a boot log live in scripts/gates/<gate>/<variant>.txt. An entry that matches nothing fails the gate, so an exemption that is no longer needed has to be removed.

Where unsafe may appear

GateFails whenCurrent figure
check_unsafe_outside_ostd.shA kernel crate other than slopos-ostd contains an unsafe keyword (comments, #[unsafe(...)] attributes and cfg-gated code excepted), or a crate in the kernel binary's dependency closure lacks #![forbid(unsafe_code)]Exempt: slopos-ostd, slopos-ostd-derive, kernel/src/main.rs, vendor/unwinding, vendor/gimli, userland crates, tools/
check_unsafe_expansion.shA kernel crate's macro expansion (-Zunpretty=expanded, every feature configuration) contains executable unsafe, or an unsafe impl, #[unsafe(link_section)] or #[unsafe(no_mangle)] not on its allowlist. A golden fixture fails it if a toolchain changes the compiler-emitted shapes it filters.See allowlists
check_safe_contract_surface.shA safe function in OSTD carries a # Safety doc section0 (baseline 0)
check_registry_sections.shThe linked kernel contains a section link.ld doesn't declare, or a linker registry's span is not a whole number of entries. The only gate that sees a link_section from a dependency.11 registries, 3 Limine sections
check_vendor_pin.shvendor/unwinding or vendor/gimli differs from its pinned upstream commit, file count or content hash2 annexes
tcb_ratio.sh --max 1.0Lines of unsafe in OSTD and the two annexes exceed 1.0 percent of kernel plus annex Rust lines. Without --max it only prints the ratio (just tcb-ratio).0.370 %

OSTD's exported obligation surface, counted with grep -rnE '^\s*pub unsafe (fn|trait)' slopos-ostd/src:

ItemCount
pub unsafe fn56
pub unsafe trait15
pub unsafe extern naked entry points (context switch, task entry, AP entry, user-mode round trip, SafeStack hook)5
Safe pub fn with a # Safety section0

Kernel crate rules

GateFails when
check_alloc_dep.shA kernel crate other than OSTD declares a direct alloc dependency
check_no_kernel_async.shA kernel crate contains async fn, an async block or async move
check_drop_panic_free.shA Drop implementation contains a direct panic, assert, unwrap or expect
check_wait_predicate_purity.shA wait-queue predicate does anything other than observe state
check_wait_result_handling.shA wait result is silenced (let _ =, .ok(), .unwrap_or*()) in a way that can drop Killed
check_task_ownership.shA raw task pointer appears outside the sanctioned OSTD primitives
check_frame_ownership.shA function takes a bare physical address, creates the owning frame from it, and can then fail
check_process_designator.shA process-keyed table entry point takes a bare u32 process id instead of a generation-checked ProcessId
check_charge_linearity.shA resource Charge can be separated from the object it accounts for: mem::forget, ManuallyDrop or .leak() on it, an Option<Charge<_>> field, .take() on a charge field, a non-minting function returning one by value, or Clone/Copy derived on a struct holding one
check_kernel_pml4_writer.shAnything other than the locked VmSpace cursor writes the kernel's master page table. Runs with the image gates on every build.
check_syscall_abi.shA syscall number disagrees with Linux x86-64's table, or a private number is not on the allowlist in scripts/gates/syscall/

Kernel image gates

These read the linked kernel ELF, so they see the effect of inlining, generics and dependencies. They run after every kernel build, for every variant (builddir/kernel-{dev,release,tests}.elf).

GateFails whenCurrent figure
check_stack_sizes.shA function's stack frame, read from .stack_sizes, exceeds the threshold and is not on the variant's allowlist; or any frame reaches the guard page, which no allowlist can exempt2048 B threshold, 4096 B guard
check_kernel_softfloat.shAn x87, MMX, SSE or AVX instruction appears outside the sanctioned save and restore. Kernel entry doesn't save user vector state, so one stray instruction corrupts the interrupted program.Allowlist scripts/gates/vector/<variant>.txt
check_registry_sections.shSee above
check_bootstrap_stack_rewind.shAnything other than its one writer changes the boot CPU's bootstrap data-stack pointer
check_authority_reachability.shA syscall handler can reach a power primitive (reboot, shutdown) in the call graph without being classified Power or listed with a reason in scripts/gates/authority/<variant>.txt. Indirect calls are not followed. Run by check-framekernel-gates on the dev kernel and by CI on the release and tests kernels, not on every build.

Boot-log ratchets

These boot the test image (or read a captured log with --log) and compare what the kernel reports with tracked limits. CI runs the first four on the log of its full test run.

GateRecipeFails whenCurrent figure
check_test_count.shjust check-test-countThe number of planned tests falls below the baselineBaseline 3603
check_lockdep_headroom.shjust check-lockdep-headroomThe lock-order validator is not ACTIVE in every phase, reports a violation, or a pool exceeds its cap in scripts/gates/lockdep/
check_sched_spread.shjust check-sched-spreadAn online CPU is not eligible for task placement
check_fs_image.shjust check-fs-imagee2fsck -fn rejects an image a boot wrote, or the image is not at rest (clean, no journal to replay)
check_fs_replay.shjust test-rude-exitThe host's e2fsck fails to replay what a killed boot committed, or the file is wrong afterwards
check_quota_headroom.shjust check-quota-headroomA resource account's peak exceeds its cap in scripts/gates/quota/, or a denial is recorded where none is expected. Not run by CI beyond its self-test.
check_fs_throughput.shjust check-fs-throughputA write costs more transactions or device requests per MiB than scripts/gates/fsperf/<variant>.txt allows. Not run by CI beyond its self-test.

Toolchain and build-input gates

GateFails when
check_toolchain_pin.shThe pinned forks of the standard library, the C library or the compiler don't match what is on disk
check_offline_build.shA registry package named in either lockfile is not vendored as the package its lockfile checksums, or something vendored is unnamed. --pins-only in check-framekernel-gates; the full offline build runs as just check-offline-build.
check_codegen_backend.shA codegen backend's capabilities (ELF output, soft-float, .stack_sizes, SafeStack, link_section, naked functions, sym operands) disagree with scripts/gates/codegen/<backend>.txt in either direction. Run for llvm and cranelift by just check-toolchain-coverage; skipped if the backend isn't installed.
check_linker_script.shA linker handles the constructs link.ld uses differently from scripts/gates/linker/<linker>.txt. Run for lld and wild; skipped if the linker isn't installed.
check_rustc_target.shThe built-in x86_64-unknown-slopos target disagrees with the JSON target spec
check_cxx_pin.shThe cross-built C++ runtime doesn't match its pin or needs symbols the C library lacks
check_llvm_port.shThe SlopOS LLVM port doesn't compile LLVM's portability layer
check_clang_driver.shThe SlopOS clang driver's link line differs from the one the build writes by hand
check_bootstrap_config.shThe Rust cross-build configuration doesn't produce the toolchain it claims to
check_recipes.shA recipe under toolchain/recipes/ isn't a pinned upstream tarball with a licence, built by a template
check_libc_license.shA crate linked into the C library has a licence MIT alone doesn't satisfy

The rustc-target, LLVM-port, clang-driver and bootstrap gates report skipped when the source tree they check hasn't been fetched; --require turns that into a failure, and CI passes it in the job that has the sources. Toolchain explains what these pieces are.

Proofs and Miri

CheckCommandFails when
Verus proofsjust verifyAny proof obligation in verification/proofs/ is unproven. See Proofs.
KernMirijust check-miriMiri reports undefined behaviour in any OSTD unit or integration test, under Stacked or Tree Borrows. See Miri.

CI runs both in the ostd-verify job.

Manual audits

Not run by check-framekernel-gates or CI.

ScriptRecipeReports
check_return_types.shjust check-return-typesKernel pub fns that return large structs by value (a heuristic regex scan)

Allowlists outside OSTD

Each entry is listed, with its reason, in the gate that allows it.

AllowedWhereGate
Global allocator and alloc-error handlerkernel/src/main.rscheck_unsafe_outside_ostd.sh, check_alloc_dep.sh
#[unsafe(no_mangle)] on kernel_main, common_exception_handler, isr_iret_frame_corruptCalled from assemblycheck_unsafe_expansion.sh
#[unsafe(link_section)] on the 11 registry sections and 3 Limine request sectionsEmitted only by registry_entry! and limine_request!check_unsafe_expansion.sh
unsafe impl of Pod, Zeroable, HermeticState, PcrStackTy, and compiler-emitted TrivialClonePod/Zeroable derived; HermeticState::restore is the one allowed unsafe fncheck_unsafe_expansion.sh
Executable unsafe in vendor/unwinding, vendor/gimliPinned annexescheck_vendor_pin.sh

On this page