# Panic-Surface Inventory

Scope: `panic!`, `assert!`, `debug_assert!`, `.unwrap()`, `.expect()`,
`todo!`, and `unreachable!` surfaces relevant to boot manifest loading, ELF
loading, SQE handling, params/result buffers, IPC, and future spawn inputs.

Classification terms:

- `trusted-internal`: depends on kernel/shared-code invariants, static ABI
  layout, or host build/test code; not directly controlled by a service.
- `boot-fatal`: reached during boot/package setup before mutually untrusted
  services run. Bad platform/package state can halt the system.
- `untrusted-input reachable`: reachable from userspace-controlled SQEs,
  Cap'n Proto params/result buffers, IPC state, manifest/package data, or
  future spawn-controlled service/binary data.

## Summary

No current `panic!`/`assert!`/`unwrap()`/`expect()` site found in the
kernel ring dispatch path directly consumes raw SQE fields or user
params/result-buffer pointers. Those paths mostly return CQE errors through
`kernel/src/cap/ring.rs`.

The remaining relevant surfaces are boot-fatal setup assumptions, scheduler
internal invariants that would become more exposed once untrusted
spawn/lifecycle inputs can create or destroy processes dynamically, and IPC
rollback queue capacity assumptions.

Locations use `path::function` anchors rather than line numbers; line numbers
drift on every refactor. Grep the path plus the quoted surface text to
re-locate a site.

## Manifest And Future Spawn Inputs

| Location | Surface | Reachability | Classification | Notes |
| --- | --- | --- | --- | --- |
| `kernel/src/main.rs` `run_init` | `MODULES.response().expect("no modules from bootloader")` | Boot package/module table | boot-fatal | Missing Limine modules abort before manifest validation. |
| `kernel/src/main.rs` `run_init` | `elf_cache.get(service.binary.as_str()).ok_or_else(...)` | Manifest service binary reference | untrusted-input reachable, controlled error | Not a panic surface. Included because it is the future spawn shape to preserve: unknown or unparsed binaries return an error. |
| `kernel/src/spawn.rs` `spawn_service` | `Process::new(...).map_err(...)` | Manifest-spawned process creation | untrusted-input reachable, controlled error | Current boot path converts allocation/mapping failures into boot errors. Future `ProcessSpawner` should keep this shape instead of adding unwraps. |

## ELF Inputs

| Location | Surface | Reachability | Classification | Notes |
| --- | --- | --- | --- | --- |
| `kernel/src/spawn.rs` `load_elf` | `debug_assert!(stack_top % 16 == 0, ...)` | ELF load path | trusted-internal | Constant stack layout invariant, not ELF-controlled. |
| `kernel/src/spawn.rs` `align_up` | `debug_assert!(align.is_power_of_two())` | TLS mapping from parsed ELF | trusted-internal | `elf::parse` rejects non-power-of-two TLS alignment; `load_tls` also caps the size before calling `align_up`. |
| `capos-lib/src/elf.rs` parser | no runtime panic surfaces outside tests/Kani | Boot manifest ELF bytes; future spawn ELF bytes | untrusted-input reachable, controlled error | Parser uses checked offsets/ranges and returns `Err(&'static str)`. Test-only assertions/unwraps are excluded from runtime classification. |
| `kernel/src/spawn.rs` `load_elf` | slice `init_data[src_offset..]` | Parsed ELF PT_LOAD file range | untrusted-input reachable, guarded | Not matched by the panic-token grep, but it is an index panic candidate if parser invariants are bypassed. `elf::parse` checks segment file ranges before `load_elf`. |
| `kernel/src/spawn.rs` `load_tls` | slice `&init_data[init_start..init_end]` | Parsed ELF TLS file range | untrusted-input reachable, guarded | Not matched by the panic-token grep, but it is an index panic candidate if parser invariants are bypassed. `elf::parse` checks TLS file bounds before `load_tls`. |

## SQE And Params/Result Buffers

| Location | Surface | Reachability | Classification | Notes |
| --- | --- | --- | --- | --- |
| `kernel/src/cap/ring.rs` `process_ring` / `dispatch_call` / `dispatch_recv` / `dispatch_return` | no matched panic-like surfaces | Userspace SQEs, params, result buffers | untrusted-input reachable, controlled error | SQ corruption, unsupported fields/opcodes, oversized buffers, invalid user buffers, and CQ pressure return transport errors or defer consumption. |
| `capos-config/src/ring.rs` `const _: () = assert!(...)` ABI size checks | const `assert!` layout checks | Shared ring ABI | trusted-internal | Compile-time ABI guard; not runtime input reachable. |
| `capos-config/src/capset.rs` `const _: () = assert!(...)` ABI size checks | const `assert!` layout checks | Shared CapSet ABI | trusted-internal | Compile-time ABI/page-fit guard; not runtime input reachable. |
| `capos-lib/src/frame_bitmap.rs` (`alloc_frame` and `alloc_contiguous`) | `.try_into().unwrap()` on 8-byte bitmap windows | Frame allocation, including work triggered by manifest/process creation and capability methods | trusted-internal | Guarded by `frame + 64 <= total` or `i + 64 <= to`, assuming the caller-provided bitmap covers `total_frames`. Kernel constructs that bitmap at boot. |

## IPC

| Location | Surface | Reachability | Classification | Notes |
| --- | --- | --- | --- | --- |
| `kernel/src/cap/endpoint.rs` `Endpoint::endpoint_call` | pending receive pop on CALL delivery | Cross-process CALL delivered to pending RECV | untrusted-input reachable, controlled error | The former guarded `pending_recvs.pop_front().unwrap()` now returns a failed capnp error if the queue is inconsistent. Endpoint pending-RECV exhaustion has QEMU coverage in `endpoint-roundtrip`. |
| `kernel/src/cap/endpoint.rs` `endpoint_restore_recv_front` | rollback `push_front` growth | IPC rollback path | untrusted-input reachable, controlled error | CALL delivery reserves the popped pending-RECV slot until rollback restores the RECV or receiver completion releases the reservation, so concurrent receives cannot consume rollback capacity. Recovery helpers resolve the original endpoint object through revoked cap epochs and wrapper recovery methods bypass liveness checks, without reopening ordinary CALL/RECV/RETURN authority. If restore still fails after reaching the endpoint, the ring path posts or defers an explicit receiver cancellation instead of silently dropping the popped RECV. `endpoint-roundtrip` includes QEMU coverage for same-process CQ-pressure rollback with both available and saturated pending-RECV capacity, then consuming the restored undersized RECV through the controlled receiver-error path; `capos-lib` host coverage checks revoked-cap recovery lookup. |

## Scheduler And Process Lifecycle

| Location | Surface | Reachability | Classification | Notes |
| --- | --- | --- | --- | --- |
| `kernel/src/sched.rs` `register_idle_process_locked` | `Process::new_idle().expect("failed to create idle process")` | Boot scheduler init (`sched_init`, slot 0) and lazy per-CPU registration (`current_cpu_idle_thread_locked`) | boot-fatal at slot 0; per-CPU-fatal on first AP idle | Synthetic idle `Process` creation OOM panics. There is no fallback idle path after the user-mode idle process removal, so this panic is the deliberate unrecoverable-OOM behavior. |
| `kernel/src/sched.rs` `sched_init` | CPL0 idle kernel stack `.expect`, idle-context registry `try_reserve_exact().expect`, per-CPU `CpuContext` `Box::try_new` `panic!` | Boot scheduler init | boot-fatal | CPL0 idle-context infrastructure OOM panics before services run. Same rationale as the synthetic idle records: no fallback idle path exists, so the failure is deliberately unrecoverable. |
| `kernel/src/sched.rs` `block_current_on_cap_enter` | `current.expect`, idle `assert!`, process-table `expect` | `cap_enter(min_complete > 0)` path | untrusted-input reachable, internal invariant | Userspace can request blocking, but these unwraps assert scheduler state, not user values. Future process lifecycle/spawn changes increase this exposure. |
| `kernel/src/sched.rs` `capos_block_current_syscall` | `current.expect`, idle `assert!`, table `expect`, `panic!` if not blocked | Blocking syscall continuation | untrusted-input reachable, internal invariant | Triggered after `cap_enter` chooses to block. User controls the request, but panic requires kernel state inconsistency. |
| `kernel/src/sched.rs` `run_queue references missing process` `expect` (context-switch + start paths) | run-queue/process-table consistency | Scheduling after queue selection | trusted-internal now; future spawn/lifecycle sensitive | A stale run-queue PID panics. Dynamic spawn/exit must preserve run-queue/process-table invariants. |
| `kernel/src/sched.rs` `exit_current` | `current.expect`, idle `assert!`, `processes.remove(...).unwrap()`, next-process `unwrap()` | Ambient `exit` syscall and future process exit | untrusted-input reachable, internal invariant | Any service can exit itself. Panic requires scheduler corruption or idle misuse, but future spawn/process APIs should harden this boundary. |
| `kernel/src/sched.rs` `current_ring_and_caps` | `current.expect`, process-table `expect` | `cap_enter` flush path | untrusted-input reachable, internal invariant | User can call `cap_enter`; panic requires no current process or missing table entry. |
| `kernel/src/sched.rs` `start` | initial run-queue `expect`, process-table `unwrap`, CR3 `expect` | Boot service start | boot-fatal | Manifest with zero services is rejected earlier, and process creation errors out; panics indicate scheduler/CR3 invariant breakage. |
| `kernel/src/arch/x86_64/context.rs` timer context restore | CR3 `expect("invalid CR3 from scheduler")` | Timer interrupt scheduling | trusted-internal; future lifecycle sensitive | Scheduler should only return page-aligned CR3s from `AddressSpace`. |

## Boot Platform And Memory Setup

| Location | Surface | Reachability | Classification | Notes |
| --- | --- | --- | --- | --- |
| `kernel/src/main.rs` `kmain` | `assert!(BASE_REVISION.is_supported())` | Limine boot protocol | boot-fatal | Platform/bootloader contract check. |
| `kernel/src/main.rs` `kmain` | memory-map and HHDM `expect` | Limine boot protocol | boot-fatal | Missing bootloader responses halt before untrusted services. |
| `kernel/src/main.rs` `kmain` | `cap::init().expect("failed to initialize kernel capabilities")` | Kernel cap table bootstrap | boot-fatal | Fails on kernel-internal cap-table exhaustion. |
| `kernel/src/mem/frame.rs` `init` | frame-bitmap region `expect("no region large enough for frame bitmap")` | Boot memory map | boot-fatal | Bad or too-small memory map halts. |
| `kernel/src/mem/frame.rs` `free_frame` | `try_free_frame(...).expect("free_frame failed")` | Kernel-owned frame teardown | trusted-internal | Capability handlers use `try_free_frame`; this panic surface is for kernel-owned frames and rollback/Drop paths. |
| `kernel/src/mem/frame.rs` HHDM cache helper | `assert!(offset != 0, "frame allocator not initialized")` | HHDM cache use before frame init | trusted-internal | Initialization-order invariant. |
| `kernel/src/mem/heap.rs` `init` | `alloc_contiguous(HEAP_FRAMES).expect("out of memory for heap")` | Boot heap init | boot-fatal | Fails if the frame allocator cannot provide the fixed kernel heap. |
| `kernel/src/mem/paging.rs` `alloc_page_table_frame` / `kernel_pml4_frame` / `assert!(addr != 0, "paging not initialized")` | page-alignment `.unwrap()` / paging initialized `assert!` | Kernel frame/page-table internals | trusted-internal | `frame::alloc_frame` returns page-aligned addresses. |
| `kernel/src/mem/paging.rs` `init_kernel_page_tables` | kernel PML4 `expect("failed to allocate kernel PML4")`, page-lookup and map `expect`s | Kernel page-table setup | boot-fatal | Assumes kernel image is mapped in bootloader tables and enough frames exist. |
| `kernel/src/arch/x86_64/syscall.rs` `init` | STAR selector `expect("invalid STAR segment configuration")` | Syscall init | boot-fatal | GDT selector layout invariant. |
| `kernel/src/sched.rs` context-switch / `exit_current` / `start` | CR3 `expect("invalid CR3")` | Context switch/exit/start | trusted-internal; future lifecycle sensitive | Scheduler should only carry page-aligned address-space roots. |

## Audit Method

Candidate sites come from panic-token searches over runtime source plus manual
review of nearby indexing and allocation paths on untrusted-input boundaries.
The table excludes test-only assertions unless they enforce runtime ABI or
layout contracts. Re-run the searches after code changes and classify new
sites by reachability, not by token alone.

Search commands:

```bash
rg -n "\b(panic!|assert!|assert_eq!|assert_ne!|debug_assert!|debug_assert_eq!|debug_assert_ne!|unwrap\(|expect\(|todo!|unreachable!)" kernel capos-lib capos-config init demos tools schema system.cue Makefile docs -g '*.rs' -g '*.cue' -g '*.md' -g 'Makefile'
rg -n "\b(panic!|assert!|assert_eq!|assert_ne!|debug_assert!|debug_assert_eq!|debug_assert_ne!|unwrap\(|expect\(|todo!|unreachable!)" kernel/src capos-lib/src capos-config/src init/src demos/capos-demo-support/src demos/*/src tools/mkmanifest/src -g '*.rs'
```
