diff --git a/.github/workflows/bone-sea-s7-allocation.yml b/.github/workflows/bone-sea-s7-allocation.yml new file mode 100644 index 0000000..e12c341 --- /dev/null +++ b/.github/workflows/bone-sea-s7-allocation.yml @@ -0,0 +1,62 @@ +name: BONE SEA S7 ALLOCATION + +on: + push: + branches: + - ecosystem/shinesea-s7-allocation-envelope + pull_request: + paths: + - config/bone_sea_s7.json + - docs/BONE_SEA_S7.md + - tools/bone-sea/** + - .github/workflows/bone-sea-s7-allocation.yml + +permissions: + contents: read + +env: + RUSTFLAGS: -Dwarnings + +jobs: + actual-core-allocation-envelope: + runs-on: ubuntu-latest + steps: + - name: Checkout exact BONEBOX branch + uses: actions/checkout@v4 + + - name: Checkout exact SHINESEA v1.0.0 source + uses: actions/checkout@v4 + with: + repository: Deadbytes101/SHINESEA + ref: 3bc6343ecaeae27f0d01c6301cab01a0e77bfb96 + path: shinesea + + - name: Record Rust identity + run: | + rustc --version --verbose + cargo --version + + - name: Test every native target with warning denial + run: | + cargo test --locked --all-targets \ + --manifest-path tools/bone-sea/Cargo.toml + + - name: Build exact deterministic S1 image + run: | + cargo run --locked --release \ + --manifest-path tools/bone-sea/Cargo.toml \ + --bin bone-sea -- \ + proof --shinesea "$GITHUB_WORKSPACE/shinesea" + + - name: Reverify shared S3 adapter + run: | + cargo run --locked --release \ + --manifest-path tools/bone-sea/Cargo.toml \ + --bin bone-sea-s3 -- verify + + - name: Execute S7 requested-live allocation envelope + run: | + cargo run --locked --release \ + --manifest-path tools/bone-sea/Cargo.toml \ + --bin bone-sea-s7 -- \ + verify --shinesea "$GITHUB_WORKSPACE/shinesea" diff --git a/config/bone_sea_s7.json b/config/bone_sea_s7.json new file mode 100644 index 0000000..cc5c1f6 --- /dev/null +++ b/config/bone_sea_s7.json @@ -0,0 +1,70 @@ +{ + "phase": "S7-ACTUAL-CORE-ALLOCATION-ENVELOPE", + "parent_head": "7cb0adf092b027a19d1bffb8a8a5e6c7dbeec4c7", + "n6_base": "93ea5ffb59ff31e87ab7fd5ecb5570c89b29ffee", + "n6_nano_sha256": "581d1a6c59580fedca7fca97035449521231f9450ca4a3c2cf35d6ed07640855", + "shinesea": { + "commit": "3bc6343ecaeae27f0d01c6301cab01a0e77bfb96", + "version": "1.0.0", + "core_mode": "no_std+alloc", + "api": "read_verified_image" + }, + "adapter": { + "module": "bone_sea::s3_adapter", + "logical_block_bytes": 4096, + "sector_bytes": 512, + "sectors_per_block": 8, + "start_lba": 40, + "last_lba": 167, + "total_sector_transfers": 144, + "atomic_publication": true, + "write_method_exposed": false + }, + "arena": { + "authority_bytes": 262144, + "source_region_start": "0x40000", + "source_region_end": "0x80000", + "measurement": "requested_live_bytes", + "backing_allocator": "std::alloc::System", + "hard_quota_enabled": true, + "allocator_metadata_measured": false, + "alignment_padding_measured": false, + "preexisting_driver_buffers_measured": false, + "final_live_bytes": 0 + }, + "success_gate": { + "verified_image_bytes": 65536, + "core_block_reads": [ + {"start_block": 1, "block_count": 2}, + {"start_block": 0, "block_count": 16} + ], + "peak_must_not_exceed_arena": true, + "quota_denials": 0, + "sector_transfers": 144 + }, + "low_budget_gate": { + "budget_bytes": 65535, + "expected_allocation_failure_bytes": 65536, + "expected_sector_transfers": 16, + "full_volume_read_allowed": false, + "final_live_bytes": 0, + "quota_denials": 1 + }, + "license_boundary": { + "driver_license": "GPL-2.0-or-later", + "retained": false, + "committed_binary": false, + "ci_artifact": false, + "public_release_authorized": false + }, + "claims": { + "host_requested_live_high_water": true, + "actual_runtime_allocator": false, + "allocator_metadata": false, + "real_ata": false, + "n6_io_gate": false, + "ring3": false, + "runtime_pass": false, + "write_authority": false + } +} diff --git a/docs/BONE_SEA_S7.md b/docs/BONE_SEA_S7.md new file mode 100644 index 0000000..c9a4a40 --- /dev/null +++ b/docs/BONE_SEA_S7.md @@ -0,0 +1,129 @@ +BONE/SEA S7 — ACTUAL-CORE ALLOCATION ENVELOPE + +STATUS + +S7 is host evidence for requested-live allocation pressure while the exact SHINESEA v1.0.0 no_std + alloc core mounts the exact S1 image through the shared S3 sector adapter. + +It is not a runtime allocator proof. + +STACKED AUTHORITY + +Parent S6 head: +7cb0adf092b027a19d1bffb8a8a5e6c7dbeec4c7 + +Exact SHINESEA source: +3bc6343ecaeae27f0d01c6301cab01a0e77bfb96 + +S2 allocator arena: +0x40000..0x80000 +262144 bytes + +S1 image: +65536 bytes +SHA256 7c2f52f99de60097d16d89dab2f583450aba9727a3a456ad028dadd561c5dab1 + +WHAT IS MEASURED + +A temporary GPL-2.0-or-later driver installs a process global quota allocator backed by std::alloc::System. + +The quota window opens immediately before actual read_verified_image and remains open until the returned VerifiedDeviceImage is dropped. + +The allocator records: + +- requested live bytes; +- peak requested live bytes; +- requested live bytes at publication; +- allocation, deallocation, and reallocation calls; +- denied requests; +- final requested live bytes after drop. + +The successful window is hard capped at 262144 requested live bytes. Any request that would raise current requested-live bytes above that limit returns a null allocation to the caller. + +The exact SHINESEA allocate_zeroed path uses Vec::try_reserve_exact, so quota denial is surfaced as DeviceImageError::AllocationFailed rather than an uncontrolled process allocation. + +WHAT IS EXCLUDED + +The following are deliberately outside the measurement: + +- allocator metadata; +- heap headers; +- alignment padding added by System; +- fragmentation; +- the S1 source bytes held by the host sector device; +- fixed sector and block trace arrays prepared before the window; +- process startup and command-line allocations; +- actual N6 allocator behavior. + +For this reason S7 reports REQUESTED LIVE BYTES ONLY and does not claim runtime allocator PASS. + +SUCCESS GATE + +The exact core must complete: + +1. read_blocks(1, 2); +2. sector transfers LBA 48..63; +3. read_blocks(0, 16); +4. sector transfers LBA 40..167; +5. exact total 144 sector transfers; +6. exact S1 byte publication; +7. peak requested-live bytes not greater than 262144; +8. zero quota denials; +9. zero requested-live bytes after VerifiedDeviceImage drop. + +LOW-BUDGET ADVERSARY + +The same actual core and shared adapter run under a 65535-byte quota. + +Expected behavior: + +- the 8192-byte superblock probe allocation succeeds; +- the probe adapter staging allocation succeeds; +- exactly 16 sector transfers occur for LBA 48..63; +- the declared 65536-byte image allocation is denied; +- read_verified_image returns AllocationFailed { bytes: 65536 }; +- the full-volume read never begins; +- requested-live bytes return to zero; +- exactly one quota denial is observed. + +LICENSE BOUNDARY + +The exact SHINESEA core and exact BONE/SEA library are compiled as separate rlibs with rustc, -Dwarnings, optimization, and panic=abort. + +Only tools/bone-sea/s7/driver.rs links them. The resulting binary is temporary GPL-2.0-or-later research output and is removed on exit. + +NON-CLAIMS + +S7 does not claim: + +- allocator metadata high-water; +- exact arena offset consumption; +- a bounded bump allocator; +- N6 allocator integration; +- real ATA; +- N6 I/O gate execution; +- CPU RIGHT_IO enforcement; +- ring3 execution; +- runtime PASS; +- write authority. + +VALIDATION + +Set RUSTFLAGS=-Dwarnings and run all native targets, rebuild the exact S1 image, then run bone-sea-s7 verify against the exact SHINESEA checkout. + +Required fixed terminals include: + +BONE/SEA S7 ALLOCATION ENVELOPE VERIFIED +ARENA AUTHORITY BYTES 262144 +FINAL LIVE BYTES 0 +SECTOR TRANSFERS 144 +LOW BUDGET BYTES 65535 +FULL IMAGE ALLOCATION REJECTED AT 65536 +LOW-BUDGET TRANSFERS 16 +ACCOUNTING REQUESTED LIVE BYTES ONLY +ALLOCATOR METADATA NOT MEASURED +WRITE AUTHORITY DISABLED +BONE/SEA S7 ACTUAL ALLOCATION VERIFY PASS +TEMPORARY DRIVER REMOVED ON EXIT + +REPORTS ARE CLAIMS, NOT EVIDENCE. +NO COSTUME RIGOR. diff --git a/tools/bone-sea/s7/LICENSE-NOTICE.txt b/tools/bone-sea/s7/LICENSE-NOTICE.txt new file mode 100644 index 0000000..fbcc747 --- /dev/null +++ b/tools/bone-sea/s7/LICENSE-NOTICE.txt @@ -0,0 +1,9 @@ +BONE/SEA S7 TEMPORARY DRIVER LICENSE NOTICE + +The file tools/bone-sea/s7/driver.rs is GPL-2.0-or-later. + +It links the exact SHINESEA v1.0.0 core and the BONE/SEA shared S3 adapter only inside a temporary local research binary. The orchestrator removes the complete temporary directory when the proof process exits. + +The combined driver binary is not retained, committed, uploaded as a CI artifact, or authorized for public distribution. + +The surrounding BONEBOX proof orchestrator and configuration remain under their repository licenses. This notice does not claim that the temporary combined binary is Apache-2.0-only. diff --git a/tools/bone-sea/s7/driver.rs b/tools/bone-sea/s7/driver.rs new file mode 100644 index 0000000..045539d --- /dev/null +++ b/tools/bone-sea/s7/driver.rs @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#![deny(warnings)] + +use std::alloc::{GlobalAlloc, Layout, System}; +use std::env; +use std::fs; +use std::path::PathBuf; +use std::process::ExitCode; +use std::ptr; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; + +use bone_sea::s3_adapter::{ + ReadOnlyBlockAdapter, SectorReader, CAPACITY_BLOCKS, CAPACITY_BYTES, LAST_LBA, SECTOR_SIZE, + START_LBA, +}; +use shinesea::{read_verified_image, BlockDevice, DeviceImageError}; + +const ARENA_BYTES: usize = 262_144; +const LOW_BUDGET_BYTES: usize = 65_535; +const DECLARED_IMAGE_BYTES: usize = 65_536; +const PROBE_SECTOR_TRANSFERS: usize = 16; +const TOTAL_SECTOR_TRANSFERS: usize = 144; +const PROBE_START_LBA: u64 = START_LBA + 8; +const PROBE_LAST_LBA: u64 = PROBE_START_LBA + 15; + +struct QuotaAllocator { + active: AtomicBool, + limit: AtomicUsize, + current: AtomicUsize, + peak: AtomicUsize, + allocations: AtomicUsize, + deallocations: AtomicUsize, + reallocations: AtomicUsize, + denied: AtomicUsize, +} + +impl QuotaAllocator { + const fn new() -> Self { + Self { + active: AtomicBool::new(false), + limit: AtomicUsize::new(0), + current: AtomicUsize::new(0), + peak: AtomicUsize::new(0), + allocations: AtomicUsize::new(0), + deallocations: AtomicUsize::new(0), + reallocations: AtomicUsize::new(0), + denied: AtomicUsize::new(0), + } + } + + fn begin(&self, limit: usize) -> bool { + if self.active.load(Ordering::SeqCst) || self.current.load(Ordering::SeqCst) != 0 { + return false; + } + self.limit.store(limit, Ordering::SeqCst); + self.peak.store(0, Ordering::SeqCst); + self.allocations.store(0, Ordering::SeqCst); + self.deallocations.store(0, Ordering::SeqCst); + self.reallocations.store(0, Ordering::SeqCst); + self.denied.store(0, Ordering::SeqCst); + self.active.store(true, Ordering::SeqCst); + true + } + + fn end(&self) -> bool { + if !self.active.load(Ordering::SeqCst) || self.current.load(Ordering::SeqCst) != 0 { + return false; + } + self.active.store(false, Ordering::SeqCst); + true + } + + fn reserve(&self, bytes: usize) -> bool { + if !self.active.load(Ordering::SeqCst) { + return true; + } + loop { + let current = self.current.load(Ordering::SeqCst); + let Some(next) = current.checked_add(bytes) else { + self.denied.fetch_add(1, Ordering::SeqCst); + return false; + }; + if next > self.limit.load(Ordering::SeqCst) { + self.denied.fetch_add(1, Ordering::SeqCst); + return false; + } + if self + .current + .compare_exchange_weak(current, next, Ordering::SeqCst, Ordering::SeqCst) + .is_ok() + { + self.raise_peak(next); + return true; + } + } + } + + fn release(&self, bytes: usize) { + if self.active.load(Ordering::SeqCst) { + let previous = self.current.fetch_sub(bytes, Ordering::SeqCst); + if previous < bytes { + std::process::abort(); + } + } + } + + fn raise_peak(&self, candidate: usize) { + let mut observed = self.peak.load(Ordering::SeqCst); + while candidate > observed { + match self.peak.compare_exchange_weak( + observed, + candidate, + Ordering::SeqCst, + Ordering::SeqCst, + ) { + Ok(_) => break, + Err(actual) => observed = actual, + } + } + } + + fn rollback(&self, bytes: usize) { + self.release(bytes); + self.denied.fetch_add(1, Ordering::SeqCst); + } + + fn snapshot(&self) -> AllocationStats { + AllocationStats { + current: self.current.load(Ordering::SeqCst), + peak: self.peak.load(Ordering::SeqCst), + allocations: self.allocations.load(Ordering::SeqCst), + deallocations: self.deallocations.load(Ordering::SeqCst), + reallocations: self.reallocations.load(Ordering::SeqCst), + denied: self.denied.load(Ordering::SeqCst), + } + } +} + +unsafe impl GlobalAlloc for QuotaAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + if !self.reserve(layout.size()) { + return ptr::null_mut(); + } + let pointer = unsafe { System.alloc(layout) }; + if pointer.is_null() { + self.rollback(layout.size()); + return pointer; + } + if self.active.load(Ordering::SeqCst) { + self.allocations.fetch_add(1, Ordering::SeqCst); + } + pointer + } + + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + if !self.reserve(layout.size()) { + return ptr::null_mut(); + } + let pointer = unsafe { System.alloc_zeroed(layout) }; + if pointer.is_null() { + self.rollback(layout.size()); + return pointer; + } + if self.active.load(Ordering::SeqCst) { + self.allocations.fetch_add(1, Ordering::SeqCst); + } + pointer + } + + unsafe fn dealloc(&self, pointer: *mut u8, layout: Layout) { + unsafe { System.dealloc(pointer, layout) }; + if self.active.load(Ordering::SeqCst) { + self.deallocations.fetch_add(1, Ordering::SeqCst); + self.release(layout.size()); + } + } + + unsafe fn realloc(&self, pointer: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + if !self.active.load(Ordering::SeqCst) { + return unsafe { System.realloc(pointer, layout, new_size) }; + } + + let old_size = layout.size(); + let growth = new_size.saturating_sub(old_size); + if growth != 0 && !self.reserve(growth) { + return ptr::null_mut(); + } + + let new_pointer = unsafe { System.realloc(pointer, layout, new_size) }; + if new_pointer.is_null() { + if growth != 0 { + self.rollback(growth); + } else { + self.denied.fetch_add(1, Ordering::SeqCst); + } + return new_pointer; + } + + if new_size < old_size { + self.release(old_size - new_size); + } + self.reallocations.fetch_add(1, Ordering::SeqCst); + new_pointer + } +} + +#[global_allocator] +static QUOTA: QuotaAllocator = QuotaAllocator::new(); + +#[derive(Clone, Copy)] +struct AllocationStats { + current: usize, + peak: usize, + allocations: usize, + deallocations: usize, + reallocations: usize, + denied: usize, +} + +struct SectorImage { + bytes: Vec, + reads: [u64; TOTAL_SECTOR_TRANSFERS], + read_count: usize, +} + +impl SectorImage { + fn new(bytes: Vec) -> Self { + Self { + bytes, + reads: [0; TOTAL_SECTOR_TRANSFERS], + read_count: 0, + } + } +} + +impl SectorReader for SectorImage { + fn read_sector(&mut self, lba: u64, output: &mut [u8]) -> Result { + if output.len() != SECTOR_SIZE { + return Err("sector output length mismatch".to_owned()); + } + if self.read_count >= self.reads.len() { + return Err("sector trace capacity exceeded".to_owned()); + } + self.reads[self.read_count] = lba; + self.read_count += 1; + + let relative = lba + .checked_sub(START_LBA) + .ok_or_else(|| "LBA before S1 extent".to_owned())?; + let offset = usize::try_from(relative) + .map_err(|_| "sector offset does not fit host address space".to_owned())? + .checked_mul(SECTOR_SIZE) + .ok_or_else(|| "sector byte offset overflow".to_owned())?; + let end = offset + .checked_add(SECTOR_SIZE) + .ok_or_else(|| "sector byte end overflow".to_owned())?; + let source = self + .bytes + .get(offset..end) + .ok_or_else(|| "LBA outside S1 extent".to_owned())?; + output.copy_from_slice(source); + Ok(SECTOR_SIZE) + } +} + +struct ComposedDevice { + adapter: ReadOnlyBlockAdapter, + block_reads: [(u64, u64); 2], + block_read_count: usize, +} + +impl ComposedDevice { + fn new(image: &[u8]) -> Self { + Self { + adapter: ReadOnlyBlockAdapter::new(SectorImage::new(image.to_vec())), + block_reads: [(0, 0); 2], + block_read_count: 0, + } + } +} + +impl BlockDevice for ComposedDevice { + type Error = String; + + fn block_count(&self) -> u64 { + self.adapter.block_count() + } + + fn read_blocks( + &mut self, + start_block: u64, + block_count: u64, + output: &mut [u8], + ) -> Result<(), Self::Error> { + if self.block_read_count >= self.block_reads.len() { + return Err("block trace capacity exceeded".to_owned()); + } + self.block_reads[self.block_read_count] = (start_block, block_count); + self.block_read_count += 1; + self.adapter.read_blocks(start_block, block_count, output) + } +} + +fn full_trace_is_exact(device: &ComposedDevice) -> bool { + if device.block_read_count != 2 + || device.block_reads != [(1, 2), (0, CAPACITY_BLOCKS)] + || device.adapter.reader().read_count != TOTAL_SECTOR_TRANSFERS + { + return false; + } + for (index, expected) in (PROBE_START_LBA..=PROBE_LAST_LBA) + .chain(START_LBA..=LAST_LBA) + .enumerate() + { + if device.adapter.reader().reads[index] != expected { + return false; + } + } + true +} + +fn probe_trace_is_exact(device: &ComposedDevice) -> bool { + if device.block_read_count != 1 + || device.block_reads[0] != (1, 2) + || device.adapter.reader().read_count != PROBE_SECTOR_TRANSFERS + { + return false; + } + for (index, expected) in (PROBE_START_LBA..=PROBE_LAST_LBA).enumerate() { + if device.adapter.reader().reads[index] != expected { + return false; + } + } + true +} + +fn measure_success(image: &[u8]) -> Result<(AllocationStats, usize), String> { + let mut device = ComposedDevice::new(image); + if !QUOTA.begin(ARENA_BYTES) { + return Err("could not begin allocation measurement".to_owned()); + } + + let result = read_verified_image(&mut device); + let mut authority_ok = false; + let mut live_at_publication = 0usize; + let stats = match result { + Ok(verified) => { + authority_ok = verified.image() == image + && verified.volume().superblock.volume_blocks == CAPACITY_BLOCKS + && full_trace_is_exact(&device); + live_at_publication = QUOTA.snapshot().current; + let observed = QUOTA.snapshot(); + drop(verified); + observed + } + Err(_) => QUOTA.snapshot(), + }; + + let final_live = QUOTA.snapshot().current; + let ended = QUOTA.end(); + if !ended { + return Err("allocation measurement did not return to zero live bytes".to_owned()); + } + if !authority_ok { + return Err("bounded actual-core mount authority mismatch".to_owned()); + } + if final_live != 0 || stats.peak == 0 || stats.peak > ARENA_BYTES || stats.denied != 0 { + return Err(format!( + "bounded allocation envelope mismatch peak={} final={} denied={}", + stats.peak, final_live, stats.denied + )); + } + Ok((stats, live_at_publication)) +} + +fn verify_low_budget_failure(image: &[u8]) -> Result { + let mut device = ComposedDevice::new(image); + if !QUOTA.begin(LOW_BUDGET_BYTES) { + return Err("could not begin low-budget measurement".to_owned()); + } + + let rejected = matches!( + read_verified_image(&mut device), + Err(DeviceImageError::AllocationFailed { + bytes: DECLARED_IMAGE_BYTES + }) + ); + let stats = QUOTA.snapshot(); + let final_live = stats.current; + let ended = QUOTA.end(); + if !ended { + return Err("low-budget measurement did not return to zero live bytes".to_owned()); + } + if !rejected + || !probe_trace_is_exact(&device) + || final_live != 0 + || stats.denied != 1 + || stats.peak > LOW_BUDGET_BYTES + { + return Err(format!( + "low-budget fail-closed mismatch rejected={rejected} peak={} final={} denied={}", + stats.peak, final_live, stats.denied + )); + } + Ok(stats) +} + +fn verify(image: Vec) -> Result<(), String> { + if image.len() != CAPACITY_BYTES || CAPACITY_BYTES != DECLARED_IMAGE_BYTES { + return Err(format!( + "S7 input authority mismatch bytes={} expected={DECLARED_IMAGE_BYTES}", + image.len() + )); + } + + let (success, live_at_publication) = measure_success(&image)?; + let low_budget = verify_low_budget_failure(&image)?; + let headroom = ARENA_BYTES + .checked_sub(success.peak) + .ok_or_else(|| "allocation headroom underflow".to_owned())?; + + println!("BONE/SEA S7 ALLOCATION ENVELOPE VERIFIED"); + println!("SHINESEA CORE ACTUAL NO_STD + ALLOC V1.0.0"); + println!("BLOCK ADAPTER SHARED S3 IMPLEMENTATION"); + println!("ARENA AUTHORITY BYTES {ARENA_BYTES}"); + println!("PEAK REQUESTED LIVE {}", success.peak); + println!("LIVE AT PUBLICATION {live_at_publication}"); + println!("ARENA HEADROOM {headroom}"); + println!("ALLOCATION CALLS {}", success.allocations); + println!("DEALLOCATION CALLS {}", success.deallocations); + println!("REALLOCATION CALLS {}", success.reallocations); + println!("FINAL LIVE BYTES 0"); + println!("SECTOR TRANSFERS {TOTAL_SECTOR_TRANSFERS}"); + println!("LOW BUDGET BYTES {LOW_BUDGET_BYTES}"); + println!("LOW BUDGET PEAK {}", low_budget.peak); + println!("FULL IMAGE ALLOCATION REJECTED AT {DECLARED_IMAGE_BYTES}"); + println!("LOW-BUDGET TRANSFERS {PROBE_SECTOR_TRANSFERS}"); + println!("ACCOUNTING REQUESTED LIVE BYTES ONLY"); + println!("ALLOCATOR METADATA NOT MEASURED"); + println!("WRITE AUTHORITY DISABLED"); + println!("COMBINED BINARY TEMPORARY GPL-2.0-OR-LATER RESEARCH ONLY"); + println!("S7 host allocation envelope complete; this is not a runtime allocator PASS claim."); + Ok(()) +} + +fn real_main() -> Result<(), String> { + let mut arguments = env::args_os().skip(1); + let image = PathBuf::from( + arguments + .next() + .ok_or_else(|| "usage: bone-sea-s7-driver IMAGE".to_owned())?, + ); + if arguments.next().is_some() { + return Err("usage: bone-sea-s7-driver IMAGE".to_owned()); + } + let bytes = fs::read(&image) + .map_err(|error| format!("cannot read {}: {error}", image.display()))?; + verify(bytes) +} + +fn main() -> ExitCode { + match real_main() { + Ok(()) => ExitCode::SUCCESS, + Err(error) => { + eprintln!("BONE/SEA S7 DRIVER ERROR: {error}"); + ExitCode::FAILURE + } + } +} diff --git a/tools/bone-sea/src/bin/bone-sea-s7.rs b/tools/bone-sea/src/bin/bone-sea-s7.rs new file mode 100644 index 0000000..2f0828b --- /dev/null +++ b/tools/bone-sea/src/bin/bone-sea-s7.rs @@ -0,0 +1,541 @@ +#![deny(warnings)] + +use std::env; +use std::ffi::{OsStr, OsString}; +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::{Command, ExitCode, Stdio}; +use std::time::{SystemTime, UNIX_EPOCH}; + +use bone_sea::sha256::sha256_file; + +const CONFIG_TEXT: &str = include_str!("../../../../config/bone_sea_s7.json"); +const PARENT_HEAD: &str = "7cb0adf092b027a19d1bffb8a8a5e6c7dbeec4c7"; +const SHINESEA_COMMIT: &str = "3bc6343ecaeae27f0d01c6301cab01a0e77bfb96"; +const SHINESEA_VERSION: &str = "SHINESEA 1.0.0"; +const IMAGE_SHA256: &str = + "7c2f52f99de60097d16d89dab2f583450aba9727a3a456ad028dadd561c5dab1"; +const IMAGE_BYTES: u64 = 65_536; +const ARENA_BYTES: usize = 262_144; +const LOW_BUDGET_BYTES: usize = 65_535; +const DECLARED_IMAGE_BYTES: usize = 65_536; +const TOTAL_SECTOR_TRANSFERS: usize = 144; +const LOW_BUDGET_TRANSFERS: usize = 16; + +type SeaResult = Result; + +struct Options { + shinesea: PathBuf, + image: Option, +} + +struct TempTree(PathBuf); + +impl TempTree { + fn create() -> SeaResult { + let stamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(|error| format!("system clock failed: {error}"))? + .as_nanos(); + for attempt in 0..128u32 { + let path = env::temp_dir().join(format!( + "bone-sea-s7-{}-{stamp}-{attempt}", + std::process::id() + )); + match fs::create_dir(&path) { + Ok(()) => return Ok(Self(path)), + Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => continue, + Err(error) => { + return Err(format!( + "cannot create temporary directory {}: {error}", + path.display() + )) + } + } + } + Err("could not allocate a unique S7 temporary directory".to_owned()) + } + + fn path(&self) -> &Path { + &self.0 + } +} + +impl Drop for TempTree { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.0); + } +} + +fn usage() -> &'static str { + "usage: cargo run --locked --release --manifest-path tools/bone-sea/Cargo.toml --bin bone-sea-s7 -- verify --shinesea PATH [--image PATH]" +} + +fn parse_options() -> SeaResult { + let mut arguments = env::args_os().skip(1); + match arguments.next().as_deref() { + Some(command) if command == OsStr::new("verify") => {} + _ => return Err(usage().to_owned()), + } + + let mut shinesea = None; + let mut image = None; + while let Some(argument) = arguments.next() { + match argument.to_string_lossy().as_ref() { + "--shinesea" => { + shinesea = Some(PathBuf::from( + arguments + .next() + .ok_or_else(|| "--shinesea requires a path".to_owned())?, + )); + } + "--image" => { + image = Some(PathBuf::from( + arguments + .next() + .ok_or_else(|| "--image requires a path".to_owned())?, + )); + } + other => return Err(format!("unknown argument: {other}")), + } + } + + Ok(Options { + shinesea: shinesea.ok_or_else(|| "--shinesea is required".to_owned())?, + image, + }) +} + +fn repository_root() -> SeaResult { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .parent() + .and_then(Path::parent) + .map(Path::to_path_buf) + .ok_or_else(|| "cannot resolve BONEBOX repository root".to_owned()) +} + +fn compiled_identity() -> SeaResult<()> { + if PARENT_HEAD.len() != 40 + || SHINESEA_COMMIT.len() != 40 + || IMAGE_SHA256.len() != 64 + || IMAGE_BYTES != 65_536 + || ARENA_BYTES != 262_144 + || LOW_BUDGET_BYTES != 65_535 + || DECLARED_IMAGE_BYTES != 65_536 + || TOTAL_SECTOR_TRANSFERS != 144 + || LOW_BUDGET_TRANSFERS != 16 + { + return Err("compiled S7 authority identity failed".to_owned()); + } + Ok(()) +} + +fn verify_config() -> SeaResult<()> { + for token in [ + "\"phase\": \"S7-ACTUAL-CORE-ALLOCATION-ENVELOPE\"", + "\"parent_head\": \"7cb0adf092b027a19d1bffb8a8a5e6c7dbeec4c7\"", + "\"commit\": \"3bc6343ecaeae27f0d01c6301cab01a0e77bfb96\"", + "\"module\": \"bone_sea::s3_adapter\"", + "\"authority_bytes\": 262144", + "\"measurement\": \"requested_live_bytes\"", + "\"backing_allocator\": \"std::alloc::System\"", + "\"hard_quota_enabled\": true", + "\"allocator_metadata_measured\": false", + "\"alignment_padding_measured\": false", + "\"preexisting_driver_buffers_measured\": false", + "\"budget_bytes\": 65535", + "\"expected_allocation_failure_bytes\": 65536", + "\"expected_sector_transfers\": 16", + "\"final_live_bytes\": 0", + "\"retained\": false", + "\"public_release_authorized\": false", + "\"actual_runtime_allocator\": false", + "\"write_authority\": false", + ] { + if !CONFIG_TEXT.contains(token) { + return Err(format!("embedded S7 config missing exact token: {token}")); + } + } + Ok(()) +} + +fn run_capture(program: &str, arguments: &[OsString], cwd: &Path) -> SeaResult { + let output = Command::new(program) + .args(arguments) + .current_dir(cwd) + .stdin(Stdio::null()) + .output() + .map_err(|error| format!("cannot run {program}: {error}"))?; + if !output.status.success() { + if !output.stdout.is_empty() { + eprint!("{}", String::from_utf8_lossy(&output.stdout)); + } + if !output.stderr.is_empty() { + eprint!("{}", String::from_utf8_lossy(&output.stderr)); + } + return Err(format!("command {program} failed with status {}", output.status)); + } + String::from_utf8(output.stdout) + .map_err(|error| format!("command output is not UTF-8: {error}")) +} + +fn run_status(program: &str, arguments: &[OsString], cwd: &Path) -> SeaResult<()> { + let status = Command::new(program) + .args(arguments) + .current_dir(cwd) + .stdin(Stdio::null()) + .status() + .map_err(|error| format!("cannot run {program}: {error}"))?; + if status.success() { + Ok(()) + } else { + Err(format!("command {program} failed with status {status}")) + } +} + +fn git_head(repository: &Path) -> SeaResult { + Ok(run_capture( + "git", + &[ + OsString::from("-C"), + repository.as_os_str().to_owned(), + OsString::from("rev-parse"), + OsString::from("HEAD"), + ], + repository, + )? + .trim() + .to_owned()) +} + +fn require_ancestor(repository: &Path, ancestor: &str, descendant: &str) -> SeaResult<()> { + let status = Command::new("git") + .arg("-C") + .arg(repository) + .arg("merge-base") + .arg("--is-ancestor") + .arg(ancestor) + .arg(descendant) + .current_dir(repository) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::piped()) + .status() + .map_err(|error| format!("cannot run git merge-base: {error}"))?; + match status.code() { + Some(0) => Ok(()), + Some(1) => Err(format!("{ancestor} is not an ancestor of {descendant}")), + other => Err(format!("git merge-base failed: {other:?}")), + } +} + +fn require_clean(repository: &Path) -> SeaResult<()> { + for cached in [false, true] { + let mut command = Command::new("git"); + command.arg("-C").arg(repository).arg("diff"); + if cached { + command.arg("--cached"); + } + let status = command + .arg("--quiet") + .arg("--ignore-submodules") + .arg("--") + .current_dir(repository) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::piped()) + .status() + .map_err(|error| format!("cannot run git diff: {error}"))?; + match status.code() { + Some(0) => {} + Some(1) => { + return Err(format!( + "tracked working tree is dirty: {}", + repository.display() + )) + } + other => return Err(format!("git cleanliness check failed: {other:?}")), + } + } + Ok(()) +} + +fn shinesea_version(shinesea: &Path) -> SeaResult { + run_capture( + "cargo", + &[ + OsString::from("run"), + OsString::from("--quiet"), + OsString::from("--locked"), + OsString::from("--release"), + OsString::from("--manifest-path"), + shinesea.join("Cargo.toml").into_os_string(), + OsString::from("--"), + OsString::from("version"), + ], + shinesea, + ) +} + +fn compile_rlib(crate_name: &str, source: PathBuf, output: &Path, cwd: &Path) -> SeaResult<()> { + run_status( + "rustc", + &[ + OsString::from("--crate-name"), + OsString::from(crate_name), + OsString::from("--crate-type=rlib"), + OsString::from("--edition=2021"), + OsString::from("-Dwarnings"), + OsString::from("-Copt-level=3"), + OsString::from("-Cpanic=abort"), + source.into_os_string(), + OsString::from("-o"), + output.as_os_str().to_owned(), + ], + cwd, + ) +} + +fn driver_path(directory: &Path) -> PathBuf { + if cfg!(windows) { + directory.join("bone-sea-s7-driver.exe") + } else { + directory.join("bone-sea-s7-driver") + } +} + +fn compile_driver( + root: &Path, + shinesea_core: &Path, + bone_sea_core: &Path, + output: &Path, +) -> SeaResult<()> { + run_status( + "rustc", + &[ + OsString::from("--edition=2021"), + OsString::from("-Dwarnings"), + OsString::from("-Copt-level=3"), + OsString::from("-Cpanic=abort"), + root.join("tools/bone-sea/s7/driver.rs").into_os_string(), + OsString::from("--extern"), + OsString::from(format!("shinesea={}", shinesea_core.display())), + OsString::from("--extern"), + OsString::from(format!("bone_sea={}", bone_sea_core.display())), + OsString::from("-o"), + output.as_os_str().to_owned(), + ], + root, + ) +} + +fn require_line(output: &str, line: &str) -> SeaResult<()> { + if output.lines().any(|candidate| candidate.trim_end() == line) { + Ok(()) + } else { + Err(format!("command output missing line: {line}")) + } +} + +fn parse_usize_line(output: &str, prefix: &str) -> SeaResult { + let line = output + .lines() + .find(|line| line.starts_with(prefix)) + .ok_or_else(|| format!("command output missing numeric prefix: {prefix}"))?; + line[prefix.len()..] + .trim() + .parse::() + .map_err(|error| format!("invalid numeric value for {prefix:?}: {error}")) +} + +fn verify(options: Options) -> SeaResult<()> { + compiled_identity()?; + verify_config()?; + + let root = repository_root()?; + let shinesea = options + .shinesea + .canonicalize() + .map_err(|error| format!("cannot resolve SHINESEA path: {error}"))?; + if !shinesea.join("Cargo.toml").is_file() || !shinesea.join("src/lib.rs").is_file() { + return Err(format!("invalid SHINESEA checkout: {}", shinesea.display())); + } + let image = options + .image + .unwrap_or_else(|| root.join("build/bone-sea-s1/shinesea-s1.img")); + + let bonebox_head = git_head(&root)?; + require_ancestor(&root, PARENT_HEAD, &bonebox_head)?; + require_clean(&root)?; + + let shinesea_head = git_head(&shinesea)?; + if shinesea_head != SHINESEA_COMMIT { + return Err(format!( + "SHINESEA head mismatch expected={SHINESEA_COMMIT} actual={shinesea_head}" + )); + } + require_clean(&shinesea)?; + if shinesea_version(&shinesea)?.trim() != SHINESEA_VERSION { + return Err("SHINESEA version mismatch".to_owned()); + } + + let metadata = fs::metadata(&image) + .map_err(|error| format!("cannot stat S1 image {}: {error}", image.display()))?; + if metadata.len() != IMAGE_BYTES { + return Err(format!( + "S1 image size mismatch expected={IMAGE_BYTES} actual={}", + metadata.len() + )); + } + let image_hash = sha256_file(&image) + .map_err(|error| format!("cannot hash S1 image {}: {error}", image.display()))?; + if image_hash != IMAGE_SHA256 { + return Err(format!( + "S1 image SHA256 mismatch expected={IMAGE_SHA256} actual={image_hash}" + )); + } + + let temporary = TempTree::create()?; + let shinesea_core = temporary.path().join("libshinesea.rlib"); + let bone_sea_core = temporary.path().join("libbone_sea.rlib"); + let driver = driver_path(temporary.path()); + compile_rlib( + "shinesea", + shinesea.join("src/lib.rs"), + &shinesea_core, + &shinesea, + )?; + compile_rlib( + "bone_sea", + root.join("tools/bone-sea/src/lib.rs"), + &bone_sea_core, + &root, + )?; + compile_driver(&root, &shinesea_core, &bone_sea_core, &driver)?; + + let output = run_capture( + driver.to_string_lossy().as_ref(), + &[image.as_os_str().to_owned()], + temporary.path(), + )?; + for line in [ + "BONE/SEA S7 ALLOCATION ENVELOPE VERIFIED", + "SHINESEA CORE ACTUAL NO_STD + ALLOC V1.0.0", + "BLOCK ADAPTER SHARED S3 IMPLEMENTATION", + "ARENA AUTHORITY BYTES 262144", + "FINAL LIVE BYTES 0", + "SECTOR TRANSFERS 144", + "LOW BUDGET BYTES 65535", + "FULL IMAGE ALLOCATION REJECTED AT 65536", + "LOW-BUDGET TRANSFERS 16", + "ACCOUNTING REQUESTED LIVE BYTES ONLY", + "ALLOCATOR METADATA NOT MEASURED", + "WRITE AUTHORITY DISABLED", + "S7 host allocation envelope complete; this is not a runtime allocator PASS claim.", + ] { + require_line(&output, line)?; + } + + let peak = parse_usize_line(&output, "PEAK REQUESTED LIVE ")?; + let live_at_publication = parse_usize_line(&output, "LIVE AT PUBLICATION ")?; + let headroom = parse_usize_line(&output, "ARENA HEADROOM ")?; + let low_budget_peak = parse_usize_line(&output, "LOW BUDGET PEAK ")?; + if peak == 0 + || peak > ARENA_BYTES + || live_at_publication == 0 + || live_at_publication > peak + || headroom != ARENA_BYTES - peak + || low_budget_peak == 0 + || low_budget_peak > LOW_BUDGET_BYTES + { + return Err(format!( + "S7 numeric authority mismatch peak={peak} live={live_at_publication} headroom={headroom} low_peak={low_budget_peak}" + )); + } + + print!("{output}"); + println!("BONE/SEA S7 ACTUAL ALLOCATION VERIFY PASS"); + println!("BONEBOX HEAD {bonebox_head}"); + println!("S7 PARENT HEAD {PARENT_HEAD}"); + println!("SHINESEA HEAD {shinesea_head}"); + println!("S1 IMAGE SHA256 {image_hash}"); + println!("ARENA AUTHORITY BYTES {ARENA_BYTES}"); + println!("PEAK REQUESTED LIVE {peak}"); + println!("ARENA HEADROOM {headroom}"); + println!("TEMPORARY DRIVER REMOVED ON EXIT"); + Ok(()) +} + +fn main() -> ExitCode { + match parse_options().and_then(verify) { + Ok(()) => ExitCode::SUCCESS, + Err(error) => { + eprintln!("BONE/SEA S7 ERROR: {error}"); + ExitCode::FAILURE + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn static_contract_passes() { + compiled_identity().unwrap(); + verify_config().unwrap(); + } + + #[test] + fn identities_are_exact() { + assert_eq!(PARENT_HEAD.len(), 40); + assert_eq!(SHINESEA_COMMIT.len(), 40); + assert_eq!(IMAGE_SHA256.len(), 64); + assert_eq!(IMAGE_BYTES, 65_536); + } + + #[test] + fn arena_authority_is_exact() { + assert_eq!(ARENA_BYTES, 262_144); + assert!(CONFIG_TEXT.contains("\"source_region_start\": \"0x40000\"")); + assert!(CONFIG_TEXT.contains("\"source_region_end\": \"0x80000\"")); + } + + #[test] + fn measurement_scope_is_explicit() { + assert!(CONFIG_TEXT.contains("\"measurement\": \"requested_live_bytes\"")); + assert!(CONFIG_TEXT.contains("\"allocator_metadata_measured\": false")); + assert!(CONFIG_TEXT.contains("\"alignment_padding_measured\": false")); + assert!(CONFIG_TEXT.contains("\"preexisting_driver_buffers_measured\": false")); + } + + #[test] + fn low_budget_gate_is_exact() { + assert_eq!(LOW_BUDGET_BYTES, 65_535); + assert_eq!(DECLARED_IMAGE_BYTES, 65_536); + assert_eq!(LOW_BUDGET_TRANSFERS, 16); + } + + #[test] + fn shared_adapter_is_frozen() { + assert!(CONFIG_TEXT.contains("\"module\": \"bone_sea::s3_adapter\"")); + assert_eq!(TOTAL_SECTOR_TRANSFERS, 144); + } + + #[test] + fn combined_binary_is_temporary_and_unreleased() { + assert!(CONFIG_TEXT.contains("\"retained\": false")); + assert!(CONFIG_TEXT.contains("\"public_release_authorized\": false")); + assert!(CONFIG_TEXT.contains("\"driver_license\": \"GPL-2.0-or-later\"")); + } + + #[test] + fn driver_name_matches_platform() { + let path = driver_path(Path::new("proof")); + if cfg!(windows) { + assert!(path.ends_with("bone-sea-s7-driver.exe")); + } else { + assert!(path.ends_with("bone-sea-s7-driver")); + } + } +}