fix(kilnd): enforce --memory at runtime + report REAL resource usage (SR-41/SR-42, closes #411 #412) - #417
Merged
Merged
Conversation
…#412) SR-41: --memory <bytes> must enforce a real runtime linear-memory cap (memory.grow bounded) — today only gates a static module_size*2 admission estimate; no engine/instance limiter exists, fix injects a byte cap clamping the effective grow max. v0.4.2. SR-42: kilnd must report REAL fuel consumed + peak/current memory + a working --memory-profile — today reports module_size/10 and module_size*2 estimates and a profiler that's never called (violates FAIL-LOUD/no-fabrication). The real data already exists (remaining_fuel(), Memory::peak_memory()/size_in_bytes()); fix is wiring. v0.4.2. Both measured on v0.4.1 with exact root-cause line numbers. Trace: SR-42
…(SR-41, SR-42, #411, #412) Two maintainer-reported bugs where kilnd faked resource accounting from module byte size (violating FAIL-LOUD/no-fabrication). SR-41 (#411): --memory <bytes> now enforces a REAL runtime linear-memory cap. - kiln-runtime Memory gains a runtime_max_pages cap (AtomicU32, 0=unlimited), checked in grow/grow_shared alongside the module-declared ty.limits.max. - kilnd sets it from --memory (default 64MB) on the instance memory after instantiate, so memory.grow past the cap now traps (was: only a load-time module_size*2 admission estimate that grow escaped entirely). SR-42 (#412): kilnd reports REAL usage, not module_size arithmetic. - CapabilityAwareEngine gains remaining_fuel() (pass-through to the inner engine); consumed = budget - remaining. - execute_traditional_module records real fuel + real peak/current memory (Memory::peak_memory()/size_in_bytes()) while the engine is live. - Removed the fabricated estimated_fuel(=size/10)/estimated_memory(=size*2) and the dead MemoryProfiler path; --memory-profile now prints the real numbers. Verified against the issue reproducers: grow-to-105MB traps at the 64MB default and succeeds under --memory 256MB (real 104923136 bytes reported, real fuel=10); sz.wat's 128MB grow traps under --memory 64MB. 3 unit oracles (rivet: verifies SR-41/SR-42); kiln-runtime 97 pass, kilnd 13 pass, no regressions. Trace: SR-41
🔍 Build Diagnostics ReportSummary
🎯 Impact AnalysisIssues in Files You Modified
Cascading Issues (Your Changes Breaking Other Files)
✅ No Issues DetectedPerfect! Your changes don't introduce any new errors or warnings, and don't break any existing code. 📊 Full diagnostic data available in workflow artifacts 🔧 To reproduce locally: # Install cargo-kiln
cargo install --path cargo-kiln
# Analyze your changes
cargo-kiln build --output json --filter-severity error
cargo-kiln check --output json --filter-severity warning |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
avrabe
added a commit
that referenced
this pull request
Jul 14, 2026
…-bounding + sigil trust chain (supersedes #416/#418/#422) (#425) * docs(rivet): capture no_std interpreter direction (AD-NOSTD-INTERP-001, amends RFC #46) Maintainer chose the hybrid direction from the #413 peer comparison: give kiln's interpreter a no_std mode for on-board embedded execution (gale/gust) ALONGSIDE the synth-AOT native path — competing head-to-head with NASA spacewasm and DLR-FT/wasm-interpreter instead of ceding that niche. - AD-NOSTD-INTERP-001 (design-decision): the hybrid decision + rationale (compiler-in-cert-TCB is harder than an interpreter; kiln already has bounded memory + fuel; std-dependence is a localized port — ~90 sync hits → kiln-sync, ~55 std:: → alloc, ~5 io → platform callbacks). Amends RFC #46's "there is no no_std interpreter"; reconciles REQ_FUNC_002/033 (marked implemented but not actually delivered). - REQ_NOSTD_INTERP (v0.6.0): the capability, phased (feasibility spike → sync/collections port → platform-callback I/O → live migration → cert parity). meld + synth are retained (build-time lowering + AOT hot path). Cross-repo: RFC #46 doc + BA sign-off, meld/synth positioning, gale/gust. Issue #415. Trace: AD-NOSTD-INTERP-001 * docs(rivet): refine no_std direction — deploy kiln AS a wasm module via synth; on-board Component Model Maintainer refinement of AD-NOSTD-INTERP-001: - DEPLOYMENT MECHANISM: kiln (no_std/no_alloc) is compiled TO a wasm module that synth AOT-compiles to native — the same pipeline as user code. wasm = universal IR, synth = single compiler in the TCB, kiln-as-wasm itself attestable via witness MC/DC + scry + sigil. No separate no_std-Rust->native toolchain needed. - UNIQUE DIFFERENTIATOR: on-board interpreter runs the FULL Component Model (direct hosting, AD-COMPONENT-HOST-001) or core wasm — spacewasm/DLR-FT are core-only. Extends AD-COMPONENT-HOST-001's direct-hosting from QM/std-only to the embedded target. - GALE (OS) requirement: load the synth-compiled kiln + support runtime wasm loading (dynamic module/component load on-target). - OPEN QUESTIONS for the Phase-1 spike: does synth accept a module as large as kiln; interpreter-in-wasm-AOT'd-to-native perf; bootstrapping trust. Fallback: direct no_std-Rust build of kiln-runtime (same port work, different codegen). REQ_NOSTD_INTERP re-phased accordingly (feasibility spike now tests kiln-as-wasm ->synth; adds Component-Model-on-embedded + gale phases). Issue #415. Trace: AD-NOSTD-INTERP-001 * docs(rivet): no_std direction — async precedent + tiered TCB + cross-link gust/gale - PRECEDENT (de-risk): kiln-async already proves the no_std/no_alloc embedded path (forbid-unsafe, thumbv7em staticlib, linked into gale, v0.3.2). The interpreter port follows an established pattern; the direct no_std-Rust build is the PROVEN baseline (low-regret). Only kiln-as-wasm->synth is spike-gated. - TIERED TCB (resolves the interpreter-vs-compiler-TCB tension): core-only minimal interpreter (small TCB, competes with peers) + opt-in Component-Model layer (differentiator). Chosen per deployment. - CROSS-REPO: added gust (gale-gust) + loom to rivet externals + synced. Discovered gust already tracks the toolchain-in-TCB trust argument (= my open-question #3) and a phase-2 kiln integration (STKH-002/SYSREQ-KILN-002/SWREQ-KILN-003); gale's north-star is CM-on-kiln/gust bare-metal via kiln-async. kiln-as-wasm->synth is philosophically identical to gust's kernel-as-dissolved-wasm. REQ_NOSTD_INTERP now depends-on gust:SYSREQ-KILN-002. The genuinely-new gust ask = RUNTIME wasm loading (interpreter path), coordinated via a gust gh issue. Trace: AD-NOSTD-INTERP-001 * chore(rivet): correct gust external git URL (gust artifacts live in the gale repo) Trace: skip * docs(rivet): capture the wasm memory-bounding story — AD-WCMC-001 (scry answers the hard half) Two fable subagents analyzed the maintainer's crux ("how much memory an arbitrary wasm can be"). Synthesis, sharpened by the maintainer's pointer that scry already answers a lot of it: - The problem splits into a DECLARED-LIMITS half (memory/table min/max, globals, data, per-fn locals + static operand-stack height — all trivially static from the module bytes) and a STACK/longest-path half (worst-case call depth + shadow-stack — the analytically hard, partly-undecidable part). - scry ("sound abstract interpretation for WebAssembly, the DO-333 leg") ALREADY does the hard half: shadow-stack bounds + longest-path + budget_from_bound. So NO new companion — compose scry(space) with spar's RTA(time). - spar already has the consumer (memory-budget pass) but hand-enters wasm sizes; scry-derived bounds make that property derived + attested. - Pipeline: meld → scry + kiln declared-limits → the EXISTING kiln.resource_limits section → {kilnd reject-at-load | synth sizes .bss/stack} → sigil attests → spar closes the space leg → rivet traces the fits verdict. Kiln-side soundness gaps found (SR-41 family, become SR-43/44/45 after #417): reject-at-load on declared min > --memory cap; a runtime element cap for tables (table.grow is uncapped — same bug SR-41 fixed for memory); WIRE the dead kiln.resource_limits section (capability_engine.rs:561 TODO); fix the 8MiB HeapProvider cloned per-function (decoded-module RSS ~8MiB×n_functions today). Registered scry in rivet externals for cross-linking. Issue #415. Trace: AD-WCMC-001 * docs(rivet): AD-WCMC-001 embedded trust chain — scry bounds signed by sigil, verified on-target scry runs host-side; the embedded target cannot recompute bounds, so it must TRUST signed ones verified on-target. Chain: scry -> kiln.resource_limits section -> sigil signs the module including it -> embedded verifies OFFLINE/ON-TARGET/ KEY-BASED -> reject-at-load off the signature-verified size info. Two sigil adjustments are the gap: (1) a no_std verifier lib (sigil is std-only today) for on-target verification on thumbv7em; (2) key-based offline only (sigil's own residual: offline keyless verification impossible). The WCMC/size manifest must be a SIGNED field, not an unsigned custom section. Coordinated via a sigil issue. Trace: AD-WCMC-001 * docs(rivet): AD-WCMC-001 — sigil verifier is a no_std LIB embedded INTO kiln's loader, not a separate tool The verify step must be a no_std library compiled INTO kiln's embedded loader — verify-signature + check-signed-size-fits + reject-at-load as ONE inseparable admission step in load_module. Not a standalone verifier the loader calls. Rationale: verification is non-bypassable (no path loads unverified bytes) and the TCB stays a single fused loader (not loader + separate verifier + IPC), which is load-bearing for the minimal-TCB argument (gust ~4-fn shim). Trace: AD-WCMC-001 * docs(rivet): AD-LOADER-VERIFY-001 — sync sigil on-target verify contract (kiln#421) Syncs the sigil-side plan from sigil#187/kiln#421 into kiln's rivet: the embedded loader verifies a sigil signature (sigil no_std verify-core, compiled INTO the loader) BEFORE trusting the signed kiln.resource_limits bounds for reject-at-load. Fail-closed: reject if sig-invalid OR section-absent OR over-budget. Confirmed cross-repo facts: loader hook = extract_resource_limits_from_binary (execution.rs:144); cargo-kiln embed-limits exists as a pre-sign step; ResourceLimitsSection reused verbatim (whole-module sig covers it); Rust staticlib boundary. sigil delivers REQ-15 (no_std verify-core v0.10.0) + REQ-16 (signed reject-at-load v0.11.0) + DD-8 (key-based Ed25519); no_std spike cleared (ed25519-compact+ct-codecs). Implements AD-WCMC-001 trust chain. Extends SR-45. Trace: AD-LOADER-VERIFY-001 EOF git push -u origin chore/loader-verify-contract 2>&1 | tail -1 * ci: re-trigger required checks (build context missed on prior push) Trace: skip
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two maintainer-reported bugs where kilnd faked resource accounting from module byte size (violates FAIL-LOUD/no-fabrication). v0.4.2.
SR-41 (#411) —
--memorynow enforces a real runtime capkiln-runtimeMemorygains aruntime_max_pagescap (AtomicU32, 0=unlimited), checked ingrow/grow_sharedalongside the module-declaredty.limits.max. kilnd sets it from--memory(default 64MB) after instantiate, somemory.growpast the cap traps — was only a load-timemodule_size*2estimate that grow escaped.SR-42 (#412) — real usage, not
module_sizearithmeticCapabilityAwareEngine::remaining_fuel()(consumed = budget − remaining);execute_traditional_modulerecords real fuel + real peak/current memory (Memory::peak_memory()/size_in_bytes()). Removed the fabricatedestimated_fuel/estimated_memoryand the deadMemoryProfiler;--memory-profileprints real numbers.Verification (issue reproducers)
--memory 256MB→ realPeak memory: 104923136, realFuel consumed: 10.--memory 64MB.rivet: verifies SR-41/SR-42); kiln-runtime 97 pass, kilnd 13 pass, no regressions.Closes #411, closes #412.
Trace: SR-41
🤖 Generated with Claude Code