Nothing to see here, move along
at main 443 lines 13 kB view raw
1use crate::cap_table::{CapRef, CapSlot}; 2use crate::error::KernelError; 3use crate::object_tag::ObjectTag; 4use crate::types::{Generation, ObjPhys}; 5 6pub const MIN_CNODE_BITS: u8 = 1; 7pub const MAX_CNODE_BITS: u8 = 15; 8pub const MAX_RESOLVE_DEPTH: u8 = 16; 9pub const CAPSLOT_SIZE: usize = core::mem::size_of::<CapSlot>(); 10pub const PAGE_SIZE: usize = 4096; 11 12const _: () = { 13 let max_slots = 1usize << MAX_CNODE_BITS; 14 let max_bytes = max_slots * CAPSLOT_SIZE; 15 let max_frames = max_bytes.div_ceil(PAGE_SIZE); 16 assert!(max_frames <= u16::MAX as usize); 17}; 18 19pub fn frames_for_cnode(size_bits: u8) -> u8 { 20 let slots = 1usize << size_bits; 21 let bytes = slots * CAPSLOT_SIZE; 22 bytes.div_ceil(PAGE_SIZE) as u8 23} 24 25pub fn extract_index(address: u64, remaining_depth: u8, size_bits: u8) -> u64 { 26 let shift = remaining_depth - size_bits; 27 let mask = (1u64 << size_bits) - 1; 28 (address >> shift) & mask 29} 30 31pub fn extract_guard(address: u64, depth: u8, guard_bits: u8) -> u64 { 32 match guard_bits { 33 0 => 0, 34 64 => address, 35 _ => { 36 let shift = depth - guard_bits; 37 let mask = (1u64 << guard_bits) - 1; 38 (address >> shift) & mask 39 } 40 } 41} 42 43pub fn validate_size_bits(size_bits: u8) -> Result<(), KernelError> { 44 match (MIN_CNODE_BITS..=MAX_CNODE_BITS).contains(&size_bits) { 45 true => Ok(()), 46 false => Err(KernelError::InvalidParameter), 47 } 48} 49 50pub trait CNodeStore { 51 fn read_slot( 52 &self, 53 cnode_phys: ObjPhys, 54 cnode_gen: Generation, 55 index: usize, 56 ) -> Result<CapSlot, KernelError>; 57 58 fn size_bits(&self, cnode_phys: ObjPhys, cnode_gen: Generation) -> Result<u8, KernelError>; 59} 60 61pub fn resolve<S: CNodeStore>( 62 store: &S, 63 cnode_phys: ObjPhys, 64 cnode_gen: Generation, 65 address: u64, 66 depth: u8, 67 guard_value: u64, 68 guard_bits: u8, 69) -> Result<CapSlot, KernelError> { 70 resolve_inner( 71 store, 72 cnode_phys, 73 cnode_gen, 74 address, 75 depth, 76 guard_value, 77 guard_bits, 78 0, 79 ) 80} 81 82#[allow(clippy::too_many_arguments)] 83fn resolve_inner<S: CNodeStore>( 84 store: &S, 85 cnode_phys: ObjPhys, 86 cnode_gen: Generation, 87 address: u64, 88 depth: u8, 89 guard_value: u64, 90 guard_bits: u8, 91 recursion: u8, 92) -> Result<CapSlot, KernelError> { 93 if recursion >= MAX_RESOLVE_DEPTH { 94 return Err(KernelError::InvalidSlot); 95 } 96 97 if depth < guard_bits { 98 return Err(KernelError::InvalidSlot); 99 } 100 101 let extracted = extract_guard(address, depth, guard_bits); 102 if extracted != guard_value { 103 return Err(KernelError::GuardMismatch); 104 } 105 106 let post_guard_depth = depth - guard_bits; 107 108 let size_bits = store.size_bits(cnode_phys, cnode_gen)?; 109 110 if post_guard_depth < size_bits { 111 return Err(KernelError::InvalidSlot); 112 } 113 114 let index = extract_index(address, post_guard_depth, size_bits) as usize; 115 let remaining = post_guard_depth - size_bits; 116 let slot = store.read_slot(cnode_phys, cnode_gen, index)?; 117 118 match remaining { 119 0 => Ok(slot), 120 _ => match slot { 121 CapSlot::Active(cap) if cap.tag() == ObjectTag::CNode => resolve_inner( 122 store, 123 cap.phys(), 124 cap.generation(), 125 address, 126 remaining, 127 cap.guard_value(), 128 cap.guard_bits(), 129 recursion + 1, 130 ), 131 _ => Err(KernelError::InvalidSlot), 132 }, 133 } 134} 135 136#[allow(clippy::too_many_arguments)] 137pub fn resolve_and_validate<S: CNodeStore>( 138 store: &S, 139 cnode_phys: ObjPhys, 140 cnode_gen: Generation, 141 address: u64, 142 depth: u8, 143 guard_value: u64, 144 guard_bits: u8, 145 expected_tag: ObjectTag, 146 required_rights: crate::cap_table::Rights, 147) -> Result<CapRef, KernelError> { 148 match resolve( 149 store, 150 cnode_phys, 151 cnode_gen, 152 address, 153 depth, 154 guard_value, 155 guard_bits, 156 )? { 157 CapSlot::Empty => Err(KernelError::SlotEmpty), 158 CapSlot::Active(cap) => { 159 if cap.tag() != expected_tag { 160 return Err(KernelError::InvalidType); 161 } 162 if !cap.rights().contains(required_rights) { 163 return Err(KernelError::InsufficientRights); 164 } 165 Ok(cap) 166 } 167 } 168} 169 170pub fn resolve_and_read<S: CNodeStore>( 171 store: &S, 172 cnode_phys: ObjPhys, 173 cnode_gen: Generation, 174 address: u64, 175 depth: u8, 176 guard_value: u64, 177 guard_bits: u8, 178) -> Result<CapRef, KernelError> { 179 match resolve( 180 store, 181 cnode_phys, 182 cnode_gen, 183 address, 184 depth, 185 guard_value, 186 guard_bits, 187 )? { 188 CapSlot::Empty => Err(KernelError::SlotEmpty), 189 CapSlot::Active(cap) => Ok(cap), 190 } 191} 192 193#[cfg(kani)] 194mod kani_proofs { 195 use super::*; 196 use crate::cap_table::Rights; 197 198 struct SingleCNode { 199 slots: [CapSlot; 16], 200 size_bits: u8, 201 expected_phys: ObjPhys, 202 expected_gen: Generation, 203 } 204 205 impl SingleCNode { 206 fn new(phys: ObjPhys, generation: Generation) -> Self { 207 Self { 208 slots: [CapSlot::Empty; 16], 209 size_bits: 4, 210 expected_phys: phys, 211 expected_gen: generation, 212 } 213 } 214 } 215 216 impl CNodeStore for SingleCNode { 217 fn read_slot( 218 &self, 219 cnode_phys: ObjPhys, 220 cnode_gen: Generation, 221 index: usize, 222 ) -> Result<CapSlot, KernelError> { 223 if cnode_phys != self.expected_phys || cnode_gen != self.expected_gen { 224 return Err(KernelError::StaleGeneration); 225 } 226 match index < (1usize << self.size_bits) { 227 true => Ok(self.slots[index]), 228 false => Err(KernelError::InvalidSlot), 229 } 230 } 231 232 fn size_bits(&self, cnode_phys: ObjPhys, cnode_gen: Generation) -> Result<u8, KernelError> { 233 if cnode_phys != self.expected_phys || cnode_gen != self.expected_gen { 234 return Err(KernelError::StaleGeneration); 235 } 236 Ok(self.size_bits) 237 } 238 } 239 240 #[kani::proof] 241 fn extract_index_bounded() { 242 let address: u64 = kani::any(); 243 let size_bits: u8 = kani::any(); 244 let remaining_depth: u8 = kani::any(); 245 246 kani::assume(size_bits >= MIN_CNODE_BITS && size_bits <= MAX_CNODE_BITS); 247 kani::assume(remaining_depth >= size_bits && remaining_depth <= 64); 248 249 let idx = extract_index(address, remaining_depth, size_bits); 250 assert!(idx < (1u64 << size_bits)); 251 } 252 253 #[kani::proof] 254 fn extract_index_shift_correctness() { 255 let size_bits: u8 = kani::any(); 256 let remaining_depth: u8 = kani::any(); 257 let address: u64 = kani::any(); 258 259 kani::assume(size_bits >= 1 && size_bits <= 8); 260 kani::assume(remaining_depth >= size_bits && remaining_depth <= 16); 261 262 let idx = extract_index(address, remaining_depth, size_bits); 263 let shift = remaining_depth - size_bits; 264 let reconstructed = idx << shift; 265 266 let mask = ((1u64 << size_bits) - 1) << shift; 267 assert!((reconstructed ^ (address & mask)) == 0); 268 } 269 270 #[kani::proof] 271 fn two_level_address_partitioning() { 272 let outer_bits: u8 = kani::any(); 273 let inner_bits: u8 = kani::any(); 274 275 kani::assume(outer_bits >= 1 && outer_bits <= 6); 276 kani::assume(inner_bits >= 1 && inner_bits <= 6); 277 278 let total_depth = outer_bits + inner_bits; 279 kani::assume(total_depth <= 12); 280 281 let outer_idx: u64 = kani::any(); 282 let inner_idx: u64 = kani::any(); 283 kani::assume(outer_idx < (1u64 << outer_bits)); 284 kani::assume(inner_idx < (1u64 << inner_bits)); 285 286 let address = (outer_idx << inner_bits) | inner_idx; 287 288 let got_outer = extract_index(address, total_depth, outer_bits); 289 let remaining = total_depth - outer_bits; 290 let got_inner = extract_index(address, remaining, inner_bits); 291 292 assert!(got_outer == outer_idx); 293 assert!(got_inner == inner_idx); 294 } 295 296 #[kani::proof] 297 fn frames_for_cnode_monotonic() { 298 let a: u8 = kani::any(); 299 let b: u8 = kani::any(); 300 kani::assume(a >= MIN_CNODE_BITS && a <= MAX_CNODE_BITS); 301 kani::assume(b >= MIN_CNODE_BITS && b <= MAX_CNODE_BITS); 302 kani::assume(a <= b); 303 assert!(frames_for_cnode(a) <= frames_for_cnode(b)); 304 } 305 306 #[kani::proof] 307 fn resolve_empty_cnode_returns_empty() { 308 let phys = ObjPhys::new(0x1000); 309 let generation = Generation::new(0); 310 let store = SingleCNode::new(phys, generation); 311 312 let address: u8 = kani::any(); 313 kani::assume(address < 16); 314 315 let result = resolve(&store, phys, generation, address as u64, 4, 0, 0); 316 assert!(matches!(result, Ok(CapSlot::Empty))); 317 } 318 319 #[kani::proof] 320 fn resolve_finds_inserted_cap() { 321 let phys = ObjPhys::new(0x1000); 322 let generation = Generation::new(0); 323 let mut store = SingleCNode::new(phys, generation); 324 325 let slot_idx: u8 = kani::any(); 326 kani::assume(slot_idx < 16); 327 328 let cap = CapRef::new( 329 ObjectTag::Endpoint, 330 ObjPhys::new(0x2000), 331 Rights::ALL, 332 Generation::new(0), 333 ); 334 store.slots[slot_idx as usize] = CapSlot::Active(cap); 335 336 let result = resolve(&store, phys, generation, slot_idx as u64, 4, 0, 0); 337 match result { 338 Ok(CapSlot::Active(found)) => { 339 assert!(found.tag() == ObjectTag::Endpoint); 340 assert!(found.phys() == ObjPhys::new(0x2000)); 341 } 342 _ => panic!("expected Active slot"), 343 } 344 } 345 346 #[kani::proof] 347 fn resolve_stale_generation_rejected() { 348 let phys = ObjPhys::new(0x1000); 349 let generation = Generation::new(0); 350 let store = SingleCNode::new(phys, generation); 351 352 let stale = Generation::new(1); 353 let result = resolve(&store, phys, stale, 0, 4, 0, 0); 354 assert!(matches!(result, Err(KernelError::StaleGeneration))); 355 } 356 357 #[kani::proof] 358 fn resolve_depth_less_than_size_bits_rejected() { 359 let phys = ObjPhys::new(0x1000); 360 let generation = Generation::new(0); 361 let store = SingleCNode::new(phys, generation); 362 363 let depth: u8 = kani::any(); 364 kani::assume(depth < 4); 365 let result = resolve(&store, phys, generation, 0, depth, 0, 0); 366 assert!(matches!(result, Err(KernelError::InvalidSlot))); 367 } 368 369 #[kani::proof] 370 fn extract_guard_bounded() { 371 let address: u64 = kani::any(); 372 let depth: u8 = kani::any(); 373 let guard_bits: u8 = kani::any(); 374 375 kani::assume(guard_bits <= 64); 376 kani::assume(depth >= guard_bits); 377 kani::assume(depth <= 64); 378 379 let val = extract_guard(address, depth, guard_bits); 380 match guard_bits { 381 0 => assert!(val == 0), 382 64 => assert!(val == address), 383 _ => assert!(val < (1u64 << guard_bits)), 384 } 385 } 386 387 #[kani::proof] 388 fn guard_mismatch_detected() { 389 let phys = ObjPhys::new(0x1000); 390 let generation = Generation::new(0); 391 let store = SingleCNode::new(phys, generation); 392 393 let result = resolve(&store, phys, generation, 0, 8, 1, 4); 394 assert!(matches!(result, Err(KernelError::GuardMismatch))); 395 } 396 397 #[kani::proof] 398 fn zero_guard_identity() { 399 let phys = ObjPhys::new(0x1000); 400 let generation = Generation::new(0); 401 let store = SingleCNode::new(phys, generation); 402 403 let address: u8 = kani::any(); 404 kani::assume(address < 16); 405 406 let with_guard = resolve(&store, phys, generation, address as u64, 4, 0, 0); 407 assert!(matches!(with_guard, Ok(CapSlot::Empty))); 408 } 409 410 #[kani::proof] 411 fn guard_plus_size_partitioning() { 412 let guard_bits: u8 = kani::any(); 413 let size_bits: u8 = kani::any(); 414 415 kani::assume(guard_bits <= 8); 416 kani::assume(size_bits >= 1 && size_bits <= 8); 417 let total = guard_bits + size_bits; 418 kani::assume(total <= 16); 419 420 let guard_val: u64 = kani::any(); 421 let index_val: u64 = kani::any(); 422 kani::assume(guard_bits == 0 || guard_val < (1u64 << guard_bits)); 423 kani::assume(index_val < (1u64 << size_bits)); 424 425 let address = (guard_val << size_bits) | index_val; 426 427 let got_guard = extract_guard(address, total, guard_bits); 428 let got_index = extract_index(address, total - guard_bits, size_bits); 429 430 assert!(got_guard == guard_val); 431 assert!(got_index == index_val); 432 } 433 434 #[kani::proof] 435 fn validate_size_bits_range() { 436 let s: u8 = kani::any(); 437 let result = validate_size_bits(s); 438 match (MIN_CNODE_BITS..=MAX_CNODE_BITS).contains(&s) { 439 true => assert!(result.is_ok()), 440 false => assert!(matches!(result, Err(KernelError::InvalidParameter))), 441 } 442 } 443}