fix(os): cover full spec behavior-descriptor set (PDF §524-525)
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.
This commit is contained in:
+77
-38
@@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}")),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user