From 0074f3112c15a4626713e536c62cffae94b41587 Mon Sep 17 00:00:00 2001 From: CUBELinux-2 Date: Thu, 13 Aug 2026 16:00:34 -0400 Subject: [PATCH] =?UTF-8?q?fix(os):=20cover=20full=20spec=20behavior-descr?= =?UTF-8?q?iptor=20set=20(PDF=20=C2=A7524-525)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cb.rs previously implemented only 3 of the 6 spec descriptors (pure/io/hot). PDF §524-525 lists: pure function, I/O heavy, allocates memory, touches network, hot path, security sensitive. - Behavior now stores descriptor bits directly as dedicated header-flag bits in the spare 8..=15 range: PURE=9, IO_HEAVY=10, ALLOCATES=11, NETWORK=13, HOT_PATH=14, SECURITY_SENSITIVE=15. Bit 12 (ENCRYPTED) left clear; mask 0xEE00. refresh_flags() preserves 8..=15 so round-trip is safe. - K= CLI tokens gain alloc/net/sec (prog + kernel verbs). - Add unit test for all-six round-trip + explicit security-sensitive bit. Proven green via ./check quick. --- cubecode/src/cb.rs | 115 +++++++++++++++++++++++++++------------- cubesys/src/commands.rs | 6 +++ 2 files changed, 83 insertions(+), 38 deletions(-) diff --git a/cubecode/src/cb.rs b/cubecode/src/cb.rs index 9e67714..2fe4126 100644 --- a/cubecode/src/cb.rs +++ b/cubecode/src/cb.rs @@ -5,9 +5,16 @@ //! discussion at PDF §524–525), functions/operators that live in CUBE are: //! * addressed in a reserved `C` axis band (here `c210..=c219`), //! * tagged with a `kind` (`fn`/`kernel`/`layer`/`checkpoint`/`variant`), and -//! * annotated with *behavior descriptor* flags (pure / I/O heavy / -//! allocates / touches-net / hot-path) carried in the record header's -//! out-of-band flag bits (tag 12, see cubestore encode/decode). +//! * annotated with *behavior descriptor* flags carried in the record +//! header's out-of-band flag bits (tag 12, see cubestore encode/decode). +//! +//! The full spec-derived descriptor set (PDF §524–525) is: "pure function," +//! "I/O heavy," "allocates memory," "touches network," "hot path," and +//! "security sensitive." We store each directly as a dedicated header-flag bit +//! inside the spare 8..=15 range, deliberately leaving bit 12 +//! (`cubecrypt::HEADER_FLAG_ENCRYPTED`) clear. `CubeHeader::refresh_flags()` +//! preserves spare bits 8..=15, so a header carrying descriptors survives an +//! encode→decode→refresh round-trip. //! //! This is deliberately NOT a store-IO model: the cubevm never reads or writes //! OS state records directly. The OS's *decisions/computation* live as CUBE @@ -24,49 +31,48 @@ pub const C_OS_KERNEL: u8 = 210; /// decides an effect (kept distinct from the compute-kernel band above). pub const C_OS_EFFECT: u8 = 211; -/// Header-flag bits 13..=15 are reserved for the behavior-descriptor field. -/// (Bit 12 is `cubecrypt::HEADER_FLAG_ENCRYPTED` and must stay clear of this -/// mask.) `HEADER_FLAG_BEHAVIOR` therefore spans exactly 0b1110_0000_0000_0000 -/// (0xE000). `refresh_flags()` preserves spare bits 8..=15, so a header -/// carrying a descriptor survives an encode→decode→refresh round-trip. -pub const HEADER_FLAG_BEHAVIOR: u16 = 0b1110_0000_0000_0000; // bits 13,14,15 +/// Header-flag bits reserved for the behavior-descriptor field. Six spec +/// descriptors, each a dedicated bit in the spare 8..=15 range, leaving bit 12 +/// (`cubecrypt::HEADER_FLAG_ENCRYPTED`) untouched: +/// PURE=9, IO_HEAVY=10, ALLOCATES=11, NETWORK=13, HOT_PATH=14, SECURITY=15. +pub const HEADER_FLAG_BEHAVIOR: u16 = + 0b1110_1110_0000_0000; // bits 9,10,11,13,14,15 (bit12 reserved) -/// Bit position of the behavior-descriptor field within the raw flag word. -pub const BEHAVIOR_SHIFT: u16 = 13; - -/// Behavior descriptors for an OS operator kernel (PDF §524–525). +/// Behavior descriptors for an OS operator kernel (PDF §524–525, full set). /// -/// These are a closed, spec-derived set: "pure function," "I/O heavy," -/// "allocates memory," "touches network," "hot path." We map them onto three -/// available out-of-band bits (13..=15) and add `Variant` (an alternate -/// implementation, one of the PDF's `Kind`s used as a descriptor tag) because -/// the OS kernel graph benefits from marking experimental variants. The set is -/// intentionally tiny and mirrors the PDF's examples rather than inventing new -/// semantics. +/// Each variant is stored directly as its dedicated header-flag bit (subset of +/// `HEADER_FLAG_BEHAVIOR`), so `Behavior` carries the raw descriptor bits and +/// round-trips through the header codec without bit-shift collisions. #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub struct Behavior(pub u8); +pub struct Behavior(pub u16); impl Behavior { /// Pure function: no side effects, deterministic on inputs. - pub const PURE: u8 = 1 << 0; + pub const PURE: u16 = 1 << 9; // 0x0200 /// I/O heavy: performs significant store/device I/O. - pub const IO_HEAVY: u8 = 1 << 1; + pub const IO_HEAVY: u16 = 1 << 10; // 0x0400 + /// Allocates memory: grows the heap / maps pages. + pub const ALLOCATES: u16 = 1 << 11; // 0x0800 + /// Touches network: performs socket/link I/O (security-relevant surface). + pub const NETWORK: u16 = 1 << 13; // 0x2000 /// Hot path: executed frequently; a candidate for optimization/variant swap. - pub const HOT_PATH: u8 = 1 << 2; + pub const HOT_PATH: u16 = 1 << 14; // 0x4000 + /// Security sensitive: elevated privilege / trust boundary crossing. + pub const SECURITY_SENSITIVE: u16 = 1 << 15; // 0x8000 - /// Build from a raw 3-bit value (already shifted into bit 13..=15 space). - pub fn from_raw(bits: u8) -> Self { - Behavior(bits & 0b111) + /// Combine descriptor bits (e.g. `Behavior::PURE | Behavior::HOT_PATH`). + pub fn new(bits: u16) -> Self { + Behavior(bits & HEADER_FLAG_BEHAVIOR) } - /// Encode into the out-of-band header flag bits (bits 13..=15). + /// Encode into the out-of-band header flag bits. pub fn to_flags(self) -> u16 { - ((self.0 & 0b111) as u16) << BEHAVIOR_SHIFT + self.0 & HEADER_FLAG_BEHAVIOR } - /// Decode from a raw 16-bit flag word (reads bits 13..=15). + /// Decode from a raw 16-bit flag word (keeps only the behavior bits). pub fn from_flags(flags: u16) -> Self { - Behavior(((flags & HEADER_FLAG_BEHAVIOR) >> BEHAVIOR_SHIFT) as u8) + Behavior(flags & HEADER_FLAG_BEHAVIOR) } /// True if any descriptor bit is set. @@ -83,9 +89,18 @@ impl Behavior { if self.0 & Self::IO_HEAVY != 0 { out.push("io"); } + if self.0 & Self::ALLOCATES != 0 { + out.push("alloc"); + } + if self.0 & Self::NETWORK != 0 { + out.push("net"); + } if self.0 & Self::HOT_PATH != 0 { out.push("hot"); } + if self.0 & Self::SECURITY_SENSITIVE != 0 { + out.push("sec"); + } out } } @@ -98,12 +113,17 @@ mod tests { #[test] fn behavior_round_trips_through_store() { - // PURE | HOT_PATH must survive the real store round-trip (what - // native-apply reads back). This is the exact field the effector - // inspects, and the gate caught it dropping the HOT_PATH bit. - let b = Behavior(Behavior::PURE | Behavior::HOT_PATH); + // All six spec descriptors must survive the real store round-trip + // (what native-apply reads back). This is the exact field the + // effector inspects; an earlier 3-bit mask (0x7000) silently dropped + // HOT_PATH (bit 15) and collided with ENCRYPTED (bit 12). + let b = Behavior::new( + Behavior::PURE | Behavior::IO_HEAVY | Behavior::ALLOCATES | Behavior::NETWORK, + ); let raw = b.to_flags(); - assert_eq!(raw, 0x2000 | 0x8000, "to_flags wrong: {raw:#x}"); + assert_eq!(raw, 0x0200 | 0x0400 | 0x0800 | 0x2000, "to_flags wrong: {raw:#x}"); + // ENCRYPTED bit (12) must never be set by a descriptor. + assert_eq!(raw & (1 << 12), 0, "descriptor must not set ENCRYPTED bit"); let mut store = CubeStore::new(HashBackend::new()); let coord = Czyx::new(210, 1, 1, 3); @@ -118,9 +138,28 @@ mod tests { let back = Behavior::from_flags(read_h.flags.bits()); assert_eq!( back, b, - "behavior dropped on store round-trip: stored {raw:#x}, got {:#x} (bits {:#x})", - read_h.flags.bits(), + "behavior dropped on store round-trip: stored {raw:#x}, got {:#x}", read_h.flags.bits() ); } + + #[test] + fn security_sensitive_bit_used_and_round_trips() { + // Explicitly cover the 6th spec descriptor (security sensitive, bit15). + let b = Behavior::new(Behavior::SECURITY_SENSITIVE | Behavior::HOT_PATH); + let mut store = CubeStore::new(HashBackend::new()); + let coord = Czyx::new(210, 1, 1, 5); + let mut h = CubeHeader::new(); + h.doc_type = Some("kernel".into()); + h.title = Some("privileged-op".into()); + h.flags.0 |= b.to_flags(); + h.refresh_flags(); + store.put_record(coord, &h, &[]); + let (read_h, _) = store.get_record(&coord).expect("record present"); + assert_eq!( + Behavior::from_flags(read_h.flags.bits()), + b, + "security-sensitive descriptor lost on round-trip" + ); + } } diff --git a/cubesys/src/commands.rs b/cubesys/src/commands.rs index e8b94a1..86b4063 100644 --- a/cubesys/src/commands.rs +++ b/cubesys/src/commands.rs @@ -461,6 +461,9 @@ impl Session { let bit = match flag { "pure" => cubecode::Behavior::PURE, "io" => cubecode::Behavior::IO_HEAVY, + "alloc" => cubecode::Behavior::ALLOCATES, + "net" => cubecode::Behavior::NETWORK, + "sec" => cubecode::Behavior::SECURITY_SENSITIVE, "hot" => cubecode::Behavior::HOT_PATH, other => return Err(format!("prog: unknown descriptor K={other}")), }; @@ -591,6 +594,9 @@ impl Session { let bit = match flag { "pure" => cubecode::Behavior::PURE, "io" => cubecode::Behavior::IO_HEAVY, + "alloc" => cubecode::Behavior::ALLOCATES, + "net" => cubecode::Behavior::NETWORK, + "sec" => cubecode::Behavior::SECURITY_SENSITIVE, "hot" => cubecode::Behavior::HOT_PATH, other => return Err(format!("kernel: unknown descriptor K={other}")), };