Microkernel based hobby OS

Stack Canaries, HEAP canaries

aethelos.iso

This is a binary file and will not be displayed.

+754
docs/RUNE_OF_PERMANENCE_DESIGN.md
··· 1 + # The Rune of Permanence - Read-Only Kernel Data Protection 2 + 3 + > *"The fundamental laws of the realm, once scribed at the Dawn of Awakening, are immutable. These crystalline structures, etched into the fabric of reality, cannot be altered—for to change them would be to rewrite the very physics of the world."* 4 + 5 + ## Overview 6 + 7 + The Rune of Permanence is AethelOS's implementation of **kernel data protection through immutability**. After the kernel completes its initialization (the "Dawn of Awakening"), critical data structures are marked as read-only at the hardware level, preventing any runtime modification—even by the kernel itself. 8 + 9 + ### The Philosophy 10 + 11 + In AethelOS, the kernel's foundational structures are not merely "protected"—they are **enchanted with permanence**. Once the realm awakens and its laws are established, those laws become as immutable as the laws of physics. The MMU (Memory Management Unit) acts as a guardian of reality itself, enforcing these constraints with hardware precision. 12 + 13 + Any attempt to modify a permanent structure is not a "security violation"—it is an attempt to **break the fabric of reality**, and the MMU denies it instantly with a page fault. 14 + 15 + --- 16 + 17 + ## Security Model 18 + 19 + ### Threat: Data-Only Attacks 20 + 21 + Even with W^X (no executable data), attackers can still compromise a system by **corrupting data structures** rather than injecting code: 22 + 23 + #### Attack Vector 1: Function Pointer Overwrites 24 + 25 + ```rust 26 + // Vulnerable dispatch table 27 + static mut SYSCALL_TABLE: [fn(); 256] = [ /* ... */ ]; 28 + 29 + // Attacker exploits buffer overflow to overwrite entry: 30 + SYSCALL_TABLE[42] = attacker_function; // ← Redirect syscall! 31 + ``` 32 + 33 + **Impact:** Attacker gains arbitrary code execution by hijacking control flow through legitimate function pointers. 34 + 35 + #### Attack Vector 2: Security Policy Corruption 36 + 37 + ```rust 38 + // Vulnerable security configuration 39 + static mut CAPABILITIES_ENABLED: bool = true; 40 + 41 + // Attacker overwrites this to disable security: 42 + CAPABILITIES_ENABLED = false; // ← Bypass capability checks! 43 + ``` 44 + 45 + **Impact:** Attacker disables security features by corrupting policy flags. 46 + 47 + #### Attack Vector 3: Critical Constant Corruption 48 + 49 + ```rust 50 + // Vulnerable kernel configuration 51 + static mut MAX_THREADS: usize = 256; 52 + 53 + // Attacker sets this to 0: 54 + MAX_THREADS = 0; // ← Denial of service! 55 + ``` 56 + 57 + **Impact:** System becomes unstable or unusable. 58 + 59 + ### Defense: The Rune of Permanence 60 + 61 + **After-Boot Immutability:** 62 + 63 + ``` 64 + ┌─────────────────────────────────────────────┐ 65 + │ Kernel Memory Layout │ 66 + ├─────────────────────────────────────────────┤ 67 + │ .text (code) │ R-X │ ← Already W^X │ 68 + ├─────────────────────────────────────────────┤ 69 + │ .rodata (constants) │ R-- │ ← Already RO │ 70 + ├─────────────────────────────────────────────┤ 71 + │ .data (variables) │ RW- │ ← Mutable │ 72 + ├─────────────────────────────────────────────┤ 73 + │ .rune (permanent) │ RW- │ ← Boot phase │ 74 + │ │ ↓ │ │ 75 + │ │ R-- │ ← After boot! │ 76 + └─────────────────────────────────────────────┘ 77 + ``` 78 + 79 + **The `.rune` section** contains structures that: 80 + 1. **Are initialized at boot** (writable during init) 81 + 2. **Never change afterward** (read-only after init) 82 + 3. **Are security-critical** (function pointers, policies, etc.) 83 + 84 + --- 85 + 86 + ## Implementation Strategy 87 + 88 + ### Phase 1: Identify Permanent Structures 89 + 90 + Categorize kernel data by mutability: 91 + 92 + #### ✓ Permanent (Mark as `.rune`) 93 + 94 + **Function Pointer Tables:** 95 + - Syscall dispatch table 96 + - Interrupt handler table (IDT entries) 97 + - Virtual function tables (vtables) 98 + - Driver callback tables 99 + 100 + **Security Policies:** 101 + - Capability enforcement flags 102 + - W^X enforcement settings 103 + - ASLR configuration 104 + - Stack canary enforcement 105 + 106 + **Kernel Configuration:** 107 + - Thread limits (MAX_THREADS) 108 + - Memory layout constants (KERNEL_BASE) 109 + - Hardware resource limits 110 + 111 + **Immutable References:** 112 + - Static references to permanent structures 113 + - Global singletons initialized at boot 114 + 115 + #### ✗ Mutable (Keep as `.data`) 116 + 117 + **Runtime State:** 118 + - Current thread ID 119 + - Scheduler state 120 + - Memory allocator free lists 121 + - I/O buffers 122 + 123 + **Statistics:** 124 + - Thread counters 125 + - Memory usage metrics 126 + - Performance timers 127 + 128 + **Lock State:** 129 + - Mutex ownership 130 + - Semaphore counts 131 + 132 + --- 133 + 134 + ### Phase 2: Rust Language Support 135 + 136 + Rust provides excellent compile-time immutability, but we need **runtime immutability** after boot. 137 + 138 + #### Option A: `const` for Compile-Time Immutability 139 + 140 + ```rust 141 + // Already immutable at compile time 142 + const KERNEL_VERSION: &str = "0.1.0-alpha"; 143 + 144 + // Compiler places this in .rodata (read-only section) 145 + ``` 146 + 147 + **Limitation:** Can't initialize complex structures at runtime (e.g., generated entropy, hardware detection). 148 + 149 + #### Option B: `static` + Custom Section for Runtime Immutability 150 + 151 + ```rust 152 + // Place in custom .rune section for post-boot protection 153 + #[link_section = ".rune"] 154 + static mut SYSCALL_TABLE: SyscallTable = SyscallTable::empty(); 155 + 156 + // During boot: Initialize (writable) 157 + unsafe { 158 + SYSCALL_TABLE.register(0, sys_read); 159 + SYSCALL_TABLE.register(1, sys_write); 160 + // ... 161 + } 162 + 163 + // After boot: Mark page as read-only 164 + unsafe { 165 + seal_permanent_structures(); 166 + } 167 + ``` 168 + 169 + **Advantages:** 170 + - Runtime initialization (can use hardware RNG, probe devices) 171 + - Explicit control over when immutability takes effect 172 + - Clear separation of permanent vs mutable data 173 + 174 + --- 175 + 176 + ### Phase 3: Memory Page Protection 177 + 178 + Use x86_64 page tables to enforce read-only protection at the hardware level. 179 + 180 + #### Page Table Entry Bits 181 + 182 + ``` 183 + ┌───────────────────────────────────────────────┐ 184 + │ Page Table Entry (x86_64) │ 185 + ├───────────────────────────────────────────────┤ 186 + │ Bit 0: Present (P) │ 1 = page valid │ 187 + │ Bit 1: Read/Write (RW) │ 1 = writable │ ← Clear this! 188 + │ Bit 2: User/Supervisor (US)│ 0 = kernel only │ 189 + │ Bit 3: Write-Through (PWT) │ │ 190 + │ Bit 4: Cache Disable (PCD) │ │ 191 + │ Bit 5: Accessed (A) │ │ 192 + │ Bit 6: Dirty (D) │ │ 193 + │ Bit 7: Page Size (PS) │ │ 194 + │ Bit 63: Execute Disable (NX)│ 1 = no execute │ 195 + └───────────────────────────────────────────────┘ 196 + ``` 197 + 198 + **To make a page read-only:** 199 + 1. Find the page table entry (PTE) for the `.rune` section 200 + 2. Clear bit 1 (Read/Write bit) → `RW = 0` 201 + 3. Flush TLB (translation lookaside buffer) to apply change 202 + 203 + #### Implementation 204 + 205 + ```rust 206 + // In mana_pool/page_tables.rs 207 + 208 + /// Seal the `.rune` section as read-only after boot 209 + /// 210 + /// # Safety 211 + /// This MUST only be called once, after all permanent structures 212 + /// have been initialized. After this call, ANY writes to the .rune 213 + /// section will cause a page fault. 214 + pub unsafe fn seal_rune_section() { 215 + extern "C" { 216 + static __rune_start: u8; 217 + static __rune_end: u8; 218 + } 219 + 220 + let start = &__rune_start as *const u8 as u64; 221 + let end = &__rune_end as *const u8 as u64; 222 + 223 + println!("◈ Sealing The Rune of Permanence..."); 224 + println!(" Range: 0x{:016x} - 0x{:016x}", start, end); 225 + 226 + // Iterate over pages in the .rune section 227 + let mut addr = start & !0xFFF; // Align to 4KB page boundary 228 + while addr < end { 229 + // Get page table entry for this address 230 + let pte = get_pte_for_address(addr); 231 + 232 + // Clear the Write bit (bit 1) 233 + let mut entry = pte.read(); 234 + entry &= !(1 << 1); // Clear RW bit → read-only 235 + pte.write(entry); 236 + 237 + // Move to next page 238 + addr += 0x1000; // 4KB page size 239 + } 240 + 241 + // Flush TLB to ensure changes take effect immediately 242 + flush_tlb(); 243 + 244 + println!(" ✓ The Rune is sealed. Permanence enforced by the MMU."); 245 + } 246 + 247 + /// Flush the Translation Lookaside Buffer (TLB) 248 + /// 249 + /// This forces the CPU to reload page table entries from memory, 250 + /// ensuring that our read-only protection takes effect immediately. 251 + #[inline(always)] 252 + fn flush_tlb() { 253 + unsafe { 254 + // Reloading CR3 flushes the entire TLB 255 + core::arch::asm!( 256 + "mov rax, cr3", 257 + "mov cr3, rax", 258 + out("rax") _, 259 + options(nostack, preserves_flags) 260 + ); 261 + } 262 + } 263 + ``` 264 + 265 + --- 266 + 267 + ### Phase 4: Linker Script Configuration 268 + 269 + Define the `.rune` section in our linker script so permanent structures are grouped together. 270 + 271 + #### Update `linker.ld` 272 + 273 + ```ld 274 + SECTIONS 275 + { 276 + . = 0x100000; /* Kernel starts at 1MB */ 277 + 278 + .text : ALIGN(4K) { 279 + *(.text .text.*) 280 + } 281 + 282 + .rodata : ALIGN(4K) { 283 + *(.rodata .rodata.*) 284 + } 285 + 286 + /* NEW: The Rune of Permanence section */ 287 + .rune : ALIGN(4K) { 288 + PROVIDE(__rune_start = .); 289 + *(.rune .rune.*) 290 + PROVIDE(__rune_end = .); 291 + } 292 + 293 + .data : ALIGN(4K) { 294 + *(.data .data.*) 295 + } 296 + 297 + .bss : ALIGN(4K) { 298 + *(.bss .bss.*) 299 + } 300 + } 301 + ``` 302 + 303 + **Key points:** 304 + - `.rune` section is page-aligned (4KB) for MMU protection 305 + - `__rune_start` and `__rune_end` symbols mark the boundaries 306 + - Placed between `.rodata` and `.data` for clarity 307 + 308 + --- 309 + 310 + ### Phase 5: Boot Sequence Integration 311 + 312 + Integrate sealing into the kernel boot process. 313 + 314 + #### Boot Flow 315 + 316 + ```rust 317 + // In main.rs 318 + 319 + fn kernel_main() -> ! { 320 + // Phase 1: Early initialization (mutable) 321 + println!("◈ Dawn of Awakening - Kernel Initialization"); 322 + 323 + init_vga_buffer(); 324 + init_gdt(); 325 + init_idt(); 326 + init_pic(); 327 + init_timer(); 328 + init_mana_pool(); 329 + 330 + // Phase 2: Initialize permanent structures (still mutable) 331 + println!("◈ Scribing the foundational runes..."); 332 + 333 + init_syscall_table(); // Populate function pointers 334 + init_security_policy(); // Set capability flags 335 + init_kernel_config(); // Set limits and constants 336 + 337 + // Phase 3: Seal permanent structures (make read-only) 338 + println!("◈ Invoking The Rune of Permanence..."); 339 + unsafe { 340 + mana_pool::page_tables::seal_rune_section(); 341 + } 342 + println!(" ✓ The foundational laws are now immutable."); 343 + 344 + // Phase 4: Start scheduler (permanent structures are now protected) 345 + println!("◈ Weaving the Loom of Fate..."); 346 + loom_of_fate::init(); 347 + 348 + // Phase 5: Enter userspace (if applicable) 349 + // ... 350 + 351 + loop { 352 + // Main kernel loop 353 + } 354 + } 355 + ``` 356 + 357 + **Critical Timing:** 358 + - **Before sealing:** Initialize all structures that go in `.rune` 359 + - **After sealing:** NEVER write to `.rune` - will cause page fault! 360 + 361 + --- 362 + 363 + ## Practical Example: Protecting the IDT 364 + 365 + The Interrupt Descriptor Table (IDT) is a prime target for attackers. Let's protect it with The Rune of Permanence. 366 + 367 + ### Current Implementation (Vulnerable) 368 + 369 + ```rust 370 + // In attunement/idt.rs 371 + 372 + // Mutable IDT in .data section (writable!) 373 + static mut IDT: InterruptDescriptorTable = InterruptDescriptorTable::new(); 374 + 375 + pub fn init() { 376 + unsafe { 377 + // Set up interrupt handlers 378 + IDT.breakpoint.set_handler_fn(breakpoint_handler); 379 + IDT.page_fault.set_handler_fn(page_fault_handler); 380 + // ... 381 + 382 + // Load IDT 383 + IDT.load(); 384 + } 385 + } 386 + ``` 387 + 388 + **Vulnerability:** Attacker with write-what-where primitive can overwrite IDT entries to hijack interrupts. 389 + 390 + ### Protected Implementation (Permanent) 391 + 392 + ```rust 393 + // In attunement/idt.rs 394 + 395 + // IDT in .rune section (read-only after boot!) 396 + #[link_section = ".rune"] 397 + static mut IDT: InterruptDescriptorTable = InterruptDescriptorTable::new(); 398 + 399 + pub fn init() { 400 + unsafe { 401 + // During boot: Initialize IDT (still writable) 402 + IDT.breakpoint.set_handler_fn(breakpoint_handler); 403 + IDT.page_fault.set_handler_fn(page_fault_handler); 404 + // ... 405 + 406 + // Load IDT 407 + IDT.load(); 408 + 409 + // NOTE: After seal_rune_section() is called, 410 + // this IDT becomes read-only and cannot be modified! 411 + } 412 + } 413 + ``` 414 + 415 + **Protection:** 416 + - Attacker can READ the IDT (see handler addresses) → Mitigated by ASLR 417 + - Attacker CANNOT WRITE to IDT → Hardware enforced (page fault) 418 + - Even kernel bugs can't accidentally corrupt IDT after boot 419 + 420 + --- 421 + 422 + ## Security Properties 423 + 424 + ### ✓ Defense-in-Depth 425 + 426 + The Rune of Permanence complements existing protections: 427 + 428 + | Protection | Defense Against | Layer | 429 + |------------|----------------|-------| 430 + | **W^X** | Code injection | Data ≠ Code | 431 + | **ASLR** | Address prediction | Randomization | 432 + | **Capability Sealing** | Capability forgery | Cryptography | 433 + | **Weaver's Sigil** | Stack overflows | Stack canaries | 434 + | **Rune of Permanence** | Data corruption | Immutability | 435 + 436 + ### ✓ Hardware Enforcement 437 + 438 + - **Not bypassable in software:** MMU enforces at CPU level 439 + - **No performance overhead:** Page permissions checked in hardware 440 + - **Fail-secure:** Write attempts cause immediate page fault 441 + 442 + ### ✓ Comprehensive Coverage 443 + 444 + Protected structures include: 445 + - ✓ IDT (interrupt handlers) 446 + - ✓ GDT (segment descriptors) 447 + - ✓ Syscall dispatch table 448 + - ✓ Security policy flags 449 + - ✓ Kernel configuration constants 450 + - ✓ Function pointer tables 451 + 452 + ### ⚠ Limitations 453 + 454 + **Not Protected:** 455 + - Runtime state (scheduler queues, memory allocators) 456 + - Performance statistics 457 + - Lock state 458 + 459 + **Attack Scenarios:** 460 + - **Physical memory access:** Attacker with DMA can modify memory 461 + - **Mitigation:** Future IOMMU support 462 + - **Speculative execution bugs:** Spectre/Meltdown-class attacks 463 + - **Mitigation:** CPU microcode updates, software mitigations 464 + - **Kernel exploits before sealing:** If attacker compromises kernel during boot 465 + - **Mitigation:** Secure boot, measured boot 466 + 467 + --- 468 + 469 + ## Testing Strategy 470 + 471 + ### Unit Tests 472 + 473 + ```rust 474 + #[cfg(test)] 475 + mod tests { 476 + use super::*; 477 + 478 + #[test] 479 + fn test_rune_section_exists() { 480 + extern "C" { 481 + static __rune_start: u8; 482 + static __rune_end: u8; 483 + } 484 + 485 + let start = unsafe { &__rune_start as *const u8 as usize }; 486 + let end = unsafe { &__rune_end as *const u8 as usize }; 487 + 488 + assert!(end > start, "Rune section must have non-zero size"); 489 + assert!(start % 0x1000 == 0, "Rune section must be page-aligned"); 490 + } 491 + 492 + #[test] 493 + #[should_panic] 494 + fn test_write_to_sealed_rune_causes_fault() { 495 + #[link_section = ".rune"] 496 + static mut TEST_VAR: u64 = 0; 497 + 498 + unsafe { 499 + // Initialize 500 + TEST_VAR = 42; 501 + 502 + // Seal 503 + seal_rune_section(); 504 + 505 + // Attempt write (should page fault!) 506 + TEST_VAR = 99; // ← PANIC! 507 + } 508 + } 509 + } 510 + ``` 511 + 512 + ### Integration Tests 513 + 514 + ```rust 515 + // In tests/permanence_test.rs 516 + 517 + #[test_case] 518 + fn test_idt_cannot_be_modified_after_boot() { 519 + // Get IDT base address 520 + let idtr = x86_64::instructions::tables::sidt(); 521 + let idt_base = idtr.base.as_ptr::<u8>(); 522 + 523 + // Attempt to write (should fail) 524 + let result = std::panic::catch_unwind(|| unsafe { 525 + core::ptr::write_volatile(idt_base, 0xFF); 526 + }); 527 + 528 + assert!(result.is_err(), "Writing to IDT after sealing should panic"); 529 + } 530 + ``` 531 + 532 + --- 533 + 534 + ## Implementation Phases 535 + 536 + ### Phase 1: Linker Script (Week 1) 537 + - [ ] Add `.rune` section to `linker.ld` 538 + - [ ] Add `__rune_start` and `__rune_end` symbols 539 + - [ ] Verify section alignment (4KB pages) 540 + - [ ] Test that section is created and non-empty 541 + 542 + ### Phase 2: Page Table Infrastructure (Week 1-2) 543 + - [ ] Implement `get_pte_for_address()` function 544 + - [ ] Implement `seal_rune_section()` function 545 + - [ ] Implement `flush_tlb()` function 546 + - [ ] Add debug logging for sealed pages 547 + - [ ] Test on dummy data before real structures 548 + 549 + ### Phase 3: Move Critical Structures (Week 2-3) 550 + - [ ] Move IDT to `.rune` section 551 + - [ ] Move GDT to `.rune` section (if applicable) 552 + - [ ] Move syscall table to `.rune` (future) 553 + - [ ] Move security policy flags to `.rune` 554 + - [ ] Test each structure after migration 555 + 556 + ### Phase 4: Boot Integration (Week 3) 557 + - [ ] Add sealing call to `kernel_main()` 558 + - [ ] Ensure all `.rune` structures initialized before sealing 559 + - [ ] Add boot message for sealing 560 + - [ ] Test that kernel boots successfully 561 + - [ ] Verify writes fail after sealing 562 + 563 + ### Phase 5: Monitoring & Validation (Week 3-4) 564 + - [ ] Add page fault handler logging for .rune violations 565 + - [ ] Add Rune of Permanence status to `wards` command 566 + - [ ] Implement debug command to inspect page permissions 567 + - [ ] Create attack simulations (try to corrupt IDT, etc.) 568 + - [ ] Performance benchmarks (should be zero overhead) 569 + 570 + --- 571 + 572 + ## Eldarin Shell Integration 573 + 574 + ### New Command: `permanence` 575 + 576 + Display protected structures and their status: 577 + 578 + ``` 579 + ◈ The Rune of Permanence - Immutable Kernel Structures 580 + 581 + Status: ✓ SEALED (hardware-enforced) 582 + Protection: MMU Read-Only Pages 583 + 584 + Protected Structures: 585 + .rune section: 0x00120000 - 0x00124000 (16 KB) 586 + Pages: 4 (all read-only) 587 + 588 + ◈ Interrupt Descriptor Table (IDT) 589 + Address: 0x00120000 590 + Size: 4096 bytes 591 + Status: ✓ Sealed (cannot modify handlers) 592 + 593 + ◈ Global Descriptor Table (GDT) 594 + Address: 0x00121000 595 + Size: 256 bytes 596 + Status: ✓ Sealed (cannot modify segments) 597 + 598 + ◈ Security Policy 599 + Address: 0x00121100 600 + Status: ✓ Sealed 601 + - Capability enforcement: ENABLED (permanent) 602 + - W^X enforcement: ENABLED (permanent) 603 + - Stack canaries: ENABLED (permanent) 604 + 605 + Violations Detected: 0 (since boot) 606 + Last Violation: None 607 + 608 + The foundational laws remain unbroken. The Rune stands eternal. 609 + ``` 610 + 611 + ### Update `wards` Command 612 + 613 + Add Rune of Permanence to the security overview: 614 + 615 + ```rust 616 + // In wards_command.rs (Page 1) 617 + 618 + crate::println!(" Rune of Permanence (Immutable Structures): ✓ Active"); 619 + crate::println!(" Protected: IDT, GDT, Security Policy"); 620 + crate::println!(" Pages: 4 read-only"); 621 + crate::println!(); 622 + ``` 623 + 624 + --- 625 + 626 + ## Page Fault Handler Enhancement 627 + 628 + Detect writes to `.rune` section and provide helpful diagnostics: 629 + 630 + ```rust 631 + // In attunement/idt_handlers.rs 632 + 633 + pub extern "x86-interrupt" fn page_fault_handler( 634 + stack_frame: InterruptStackFrame, 635 + error_code: PageFaultErrorCode, 636 + ) { 637 + use x86_64::registers::control::Cr2; 638 + 639 + // Get the address that caused the fault 640 + let fault_addr = Cr2::read().as_u64(); 641 + 642 + // Check if this was a write to the .rune section 643 + extern "C" { 644 + static __rune_start: u8; 645 + static __rune_end: u8; 646 + } 647 + let rune_start = unsafe { &__rune_start as *const u8 as u64 }; 648 + let rune_end = unsafe { &__rune_end as *const u8 as u64 }; 649 + 650 + if fault_addr >= rune_start && fault_addr < rune_end { 651 + if error_code.contains(PageFaultErrorCode::CAUSED_BY_WRITE) { 652 + // Write to sealed section! 653 + panic!( 654 + "◈ RUNE VIOLATION: Attempt to modify permanent structure!\n\ 655 + Address: 0x{:016x}\n\ 656 + Section: .rune (read-only)\n\ 657 + Error: Write to immutable memory after Dawn of Awakening\n\ 658 + \n\ 659 + The Rune of Permanence has been violated. The foundational \n\ 660 + laws cannot be rewritten. The MMU denies this reality break.", 661 + fault_addr 662 + ); 663 + } 664 + } 665 + 666 + // Standard page fault handling... 667 + panic!("Page fault at 0x{:016x}: {:?}", fault_addr, error_code); 668 + } 669 + ``` 670 + 671 + --- 672 + 673 + ## Future Enhancements 674 + 675 + ### Incremental Sealing 676 + 677 + Instead of sealing everything at once, seal structures incrementally: 678 + 679 + ```rust 680 + pub fn seal_idt() { 681 + seal_range(&IDT as *const _ as u64, size_of::<IDT>()); 682 + } 683 + 684 + pub fn seal_gdt() { 685 + seal_range(&GDT as *const _ as u64, size_of::<GDT>()); 686 + } 687 + ``` 688 + 689 + **Benefit:** More granular control, easier debugging. 690 + 691 + ### Runtime Re-initialization (Emergency Mode) 692 + 693 + For debugging or emergency patches, temporarily unseal: 694 + 695 + ```rust 696 + pub unsafe fn unseal_rune_section_emergency(auth_token: &EmergencyAuth) { 697 + // Verify cryptographic auth token 698 + if !verify_emergency_auth(auth_token) { 699 + panic!("Unauthorized unseal attempt!"); 700 + } 701 + 702 + // Log the event 703 + log::warn!("EMERGENCY: Unsealing .rune section"); 704 + 705 + // Temporarily make writable 706 + // ... (reverse the sealing process) 707 + } 708 + ``` 709 + 710 + **Use case:** Hot-patching critical vulnerabilities without reboot. 711 + 712 + ### Signature Verification 713 + 714 + Before sealing, compute a cryptographic hash of `.rune` section: 715 + 716 + ```rust 717 + pub fn seal_with_verification() { 718 + // Compute hash before sealing 719 + let hash = compute_rune_hash(); 720 + 721 + // Seal the section 722 + seal_rune_section(); 723 + 724 + // Store hash in secure location 725 + store_rune_hash(hash); 726 + } 727 + 728 + pub fn verify_rune_integrity() -> bool { 729 + let current_hash = compute_rune_hash(); 730 + let stored_hash = retrieve_rune_hash(); 731 + 732 + constant_time_compare(&current_hash, &stored_hash) 733 + } 734 + ``` 735 + 736 + **Benefit:** Detect if `.rune` was modified before sealing (bootkit attack). 737 + 738 + --- 739 + 740 + ## References 741 + 742 + - **PaX CONSTIFY:** https://pax.grsecurity.net/docs/constify.txt 743 + - **Linux RODATA:** https://lwn.net/Articles/666550/ 744 + - **x86_64 Paging:** Intel SDM Volume 3A, Chapter 4 745 + - **Rust Linker Scripts:** https://doc.rust-lang.org/rustc/codegen-options/index.html#link-args 746 + 747 + --- 748 + 749 + *"The Rune of Permanence is etched not in ink, but in the silicon itself. It is enforced not by software, but by the immutable laws of the MMU. To break it is to break reality—and reality does not yield."* 750 + 751 + **Status:** 🚧 Design Complete, Implementation Pending 752 + **Priority:** High (Security Critical) 753 + **Estimated Effort:** 3-4 weeks 754 + **Dependencies:** Functional page table management
+610
docs/WEAVERS_SIGIL_DESIGN.md
··· 1 + # The Weaver's Sigil - Stack Canary Protection Design 2 + 3 + > *"Each thread weaves its own fate, and marks it with a sigil of purity. Should that mark be corrupted, the thread knows its fate has been tampered with by chaotic forces."* 4 + 5 + ## Overview 6 + 7 + The Weaver's Sigil is AethelOS's implementation of **stack canary protection**, a runtime defense against buffer overflow attacks that attempt to overwrite return addresses on the stack. 8 + 9 + ### The Philosophy 10 + 11 + In AethelOS, each thread is a strand in the Loom of Fate. When a thread begins weaving a new spell (calling a function), it places a unique **magical sigil** on its own thread of fate (the stack). This sigil is: 12 + 13 + - **Personal**: Unique per-thread to prevent cross-thread attacks 14 + - **Secret**: Unknown to userspace, preventing predictable overwrites 15 + - **Immutable**: Any change indicates corruption 16 + 17 + Before completing the spell (returning from the function), the thread inspects its sigil. If corrupted, the thread immediately enters **quarantine** (panic), containing the damage before it spreads. 18 + 19 + --- 20 + 21 + ## Security Model 22 + 23 + ### Threat: Stack Buffer Overflows 24 + 25 + **Attack Vector:** 26 + ``` 27 + Stack Layout (grows downward): 28 + ┌─────────────────┐ ← High addresses 29 + │ Return Address │ ← Attacker wants to overwrite this! 30 + ├─────────────────┤ 31 + │ Saved RBP │ 32 + ├─────────────────┤ 33 + │ Local Buffer │ ← Overflow starts here 34 + └─────────────────┘ ← Low addresses 35 + ``` 36 + 37 + If an attacker can overflow `Local Buffer`, they can: 38 + 1. Overwrite the saved return address 39 + 2. Redirect execution to attacker-controlled code 40 + 3. Gain arbitrary code execution 41 + 42 + ### Defense: The Weaver's Sigil 43 + 44 + **Protected Stack Layout:** 45 + ``` 46 + Stack Layout (grows downward): 47 + ┌─────────────────┐ ← High addresses 48 + │ Return Address │ ← Protected by canary below 49 + ├─────────────────┤ 50 + │ Saved RBP │ 51 + ├─────────────────┤ 52 + │ CANARY (8 bytes)│ ← The Weaver's Sigil! Random, secret value 53 + ├─────────────────┤ 54 + │ Local Buffer │ ← Overflow must corrupt canary to reach return address 55 + └─────────────────┘ ← Low addresses 56 + ``` 57 + 58 + **Function Prologue (Entry):** 59 + ```rust 60 + // 1. Load thread-local canary 61 + let canary = current_thread().sigil; 62 + 63 + // 2. Push canary onto stack 64 + push(canary); 65 + 66 + // 3. Continue with normal prologue (save RBP, allocate locals, etc.) 67 + ``` 68 + 69 + **Function Epilogue (Exit):** 70 + ```rust 71 + // 1. Load canary from stack 72 + let stack_canary = pop_canary(); 73 + 74 + // 2. Compare with thread-local canary 75 + if stack_canary != current_thread().sigil { 76 + panic!("◈ SIGIL CORRUPTED: Stack overflow detected!"); 77 + } 78 + 79 + // 3. Continue with normal epilogue (restore RBP, return) 80 + ``` 81 + 82 + --- 83 + 84 + ## Implementation Strategy 85 + 86 + ### Phase 1: Infrastructure (Canary Storage) 87 + 88 + **Per-Thread Canary Storage:** 89 + 90 + Each `Thread` structure stores its unique Weaver's Sigil: 91 + 92 + ```rust 93 + // In loom_of_fate/mod.rs 94 + pub struct Thread { 95 + // ... existing fields ... 96 + 97 + /// The Weaver's Sigil - unique per-thread stack canary 98 + /// This value is secret and should never be exposed to userspace 99 + pub(crate) sigil: u64, 100 + } 101 + ``` 102 + 103 + **Canary Generation:** 104 + 105 + Use existing entropy infrastructure (ChaCha8Rng with RDTSC): 106 + 107 + ```rust 108 + // In loom_of_fate/mod.rs 109 + impl Thread { 110 + pub fn new(entry_point: fn() -> !, priority: ThreadPriority) -> Self { 111 + // Generate unique sigil for this thread 112 + let sigil = { 113 + let mut rng = mana_pool::entropy::ChaCha8Rng::from_hardware_fast(); 114 + let high = rng.next_u32() as u64; 115 + let low = rng.next_u32() as u64; 116 + (high << 32) | low 117 + }; 118 + 119 + Thread { 120 + // ... other fields ... 121 + sigil, 122 + } 123 + } 124 + } 125 + ``` 126 + 127 + **Thread-Local Access:** 128 + 129 + Provide fast access to current thread's sigil: 130 + 131 + ```rust 132 + // In loom_of_fate/mod.rs 133 + 134 + /// Get the current thread's Weaver's Sigil (stack canary) 135 + /// 136 + /// # Safety 137 + /// This function is unsafe because it accesses thread-local state. 138 + /// It MUST only be called from kernel code, never exposed to userspace. 139 + #[inline(always)] 140 + pub unsafe fn get_current_sigil() -> u64 { 141 + get_loom().lock().current_thread().sigil 142 + } 143 + ``` 144 + 145 + --- 146 + 147 + ### Phase 2: Compiler Integration 148 + 149 + Rust doesn't natively support stack canaries for custom targets like `x86_64-aethelos.json`. We have two approaches: 150 + 151 + #### Option A: LLVM Stack Protector (Preferred) 152 + 153 + Enable LLVM's built-in stack protector in our target JSON: 154 + 155 + ```json 156 + { 157 + "llvm-target": "x86_64-unknown-none", 158 + "target-endian": "little", 159 + "target-pointer-width": "64", 160 + "features": "-mmx,-sse,+soft-float", 161 + "disable-redzone": true, 162 + "panic-strategy": "abort", 163 + 164 + "stack-probes": { 165 + "kind": "inline-or-call", 166 + "min-llvm-version-for-inline": [16, 0, 0] 167 + }, 168 + 169 + "// NEW": "Enable LLVM stack protector", 170 + "stack-protector": "strong" 171 + } 172 + ``` 173 + 174 + **Stack Protector Modes:** 175 + - `"basic"` - Protect functions with buffers > 8 bytes 176 + - `"strong"` - Protect all functions with local arrays or address-taken locals 177 + - `"all"` - Protect ALL functions (performance overhead) 178 + 179 + **For AethelOS:** Use `"strong"` mode for security without protecting trivial functions. 180 + 181 + **Implement `__stack_chk_fail`:** 182 + 183 + LLVM expects a symbol `__stack_chk_fail` to be called when canary check fails: 184 + 185 + ```rust 186 + // In lib.rs or boot.rs 187 + 188 + /// Stack canary failure handler 189 + /// Called by LLVM when a stack corruption is detected 190 + #[no_mangle] 191 + pub extern "C" fn __stack_chk_fail() -> ! { 192 + // Log the corruption 193 + serial_println!("◈ FATAL: The Weaver's Sigil has been corrupted!"); 194 + serial_println!(" Stack overflow detected. Thread integrity compromised."); 195 + 196 + // Get current thread info for debugging 197 + unsafe { 198 + if let Some(thread) = loom_of_fate::get_current_thread_debug_info() { 199 + serial_println!(" Thread: #{} ({})", thread.id, thread.name); 200 + serial_println!(" Stack: 0x{:016x} - 0x{:016x}", 201 + thread.stack_bottom, thread.stack_top); 202 + } 203 + } 204 + 205 + // Panic with stack trace 206 + panic!("Stack canary violation - buffer overflow detected!"); 207 + } 208 + ``` 209 + 210 + **Implement `__stack_chk_guard`:** 211 + 212 + LLVM uses a global symbol `__stack_chk_guard` as the canary value. We need to provide this and populate it per-thread: 213 + 214 + ```rust 215 + // In lib.rs 216 + 217 + /// Global stack canary value (thread-local via TLS) 218 + /// LLVM uses this symbol for stack protection 219 + #[no_mangle] 220 + pub static mut __stack_chk_guard: u64 = 0xDEADBEEF_CAFEBABE; // Placeholder 221 + 222 + /// Initialize stack canary for current thread 223 + /// Called when switching to a new thread 224 + pub unsafe fn init_thread_canary(thread_sigil: u64) { 225 + core::ptr::write_volatile(&mut __stack_chk_guard, thread_sigil); 226 + } 227 + ``` 228 + 229 + **Thread Context Switch Integration:** 230 + 231 + Update scheduler to set canary when switching threads: 232 + 233 + ```rust 234 + // In loom_of_fate/mod.rs - schedule() function 235 + 236 + fn schedule(&mut self) { 237 + // ... existing scheduling logic ... 238 + 239 + let next_thread = self.find_next_thread(); 240 + 241 + // Update stack canary for new thread 242 + unsafe { 243 + crate::init_thread_canary(next_thread.sigil); 244 + } 245 + 246 + // ... continue with context switch ... 247 + } 248 + ``` 249 + 250 + --- 251 + 252 + #### Option B: Manual Instrumentation (Fallback) 253 + 254 + If LLVM stack protector doesn't work, we manually instrument critical functions: 255 + 256 + **Macro for Protected Functions:** 257 + 258 + ```rust 259 + /// Macro to add stack canary protection to a function 260 + /// 261 + /// Usage: 262 + /// ``` 263 + /// #[stack_protected] 264 + /// fn vulnerable_function(buffer: &mut [u8]) { 265 + /// // ... potentially unsafe buffer operations ... 266 + /// } 267 + /// ``` 268 + #[macro_export] 269 + macro_rules! stack_protected { 270 + (fn $name:ident($($args:tt)*) -> $ret:ty $body:block) => { 271 + fn $name($($args)*) -> $ret { 272 + // Prologue: Save canary on stack 273 + let __sigil = unsafe { $crate::loom_of_fate::get_current_sigil() }; 274 + 275 + // Execute function body 276 + let __result = (|| $body)(); 277 + 278 + // Epilogue: Check canary 279 + let __current_sigil = unsafe { $crate::loom_of_fate::get_current_sigil() }; 280 + if __sigil != __current_sigil { 281 + panic!("Stack canary violation in {}", stringify!($name)); 282 + } 283 + 284 + __result 285 + } 286 + }; 287 + } 288 + ``` 289 + 290 + **Usage:** 291 + 292 + ```rust 293 + stack_protected! { 294 + fn parse_user_input(buffer: &mut [u8; 256]) -> Result<Command, ParseError> { 295 + // Function is now protected by stack canary 296 + // ... 297 + } 298 + } 299 + ``` 300 + 301 + --- 302 + 303 + ### Phase 3: Kernel Integration 304 + 305 + **1. Boot Initialization:** 306 + 307 + ```rust 308 + // In main.rs 309 + 310 + fn kernel_main() { 311 + // ... existing init ... 312 + 313 + // Initialize Weaver's Sigil protection 314 + println!("◈ Weaving protective sigils..."); 315 + unsafe { 316 + // Set initial canary for boot thread 317 + let boot_sigil = 0xBADC0FFE_DEADBEEF; // Random initial value 318 + crate::init_thread_canary(boot_sigil); 319 + } 320 + println!(" ✓ The Weaver's Sigil protects the kernel"); 321 + 322 + // ... continue boot ... 323 + } 324 + ``` 325 + 326 + **2. Thread Creation:** 327 + 328 + ```rust 329 + // In loom_of_fate/mod.rs 330 + 331 + impl LoomOfFate { 332 + pub fn spawn(&mut self, entry: fn() -> !, priority: ThreadPriority) 333 + -> Result<ThreadId, LoomError> { 334 + 335 + // Create thread (generates unique sigil) 336 + let thread = Thread::new(entry, priority); 337 + 338 + serial_println!("◈ New thread sigil: 0x{:016x}", thread.sigil); 339 + 340 + // ... rest of spawn logic ... 341 + } 342 + } 343 + ``` 344 + 345 + **3. Context Switch:** 346 + 347 + Already covered above - update `__stack_chk_guard` on every context switch. 348 + 349 + --- 350 + 351 + ### Phase 4: Userspace Protection (Future) 352 + 353 + When userspace processes are implemented: 354 + 355 + 1. **Separate Canaries:** Each process has its own canary namespace 356 + 2. **Syscall Barrier:** Kernel saves/restores `__stack_chk_guard` on syscall entry/exit 357 + 3. **ASLR Integration:** Combine with ASLR for layered defense 358 + 359 + --- 360 + 361 + ## Security Properties 362 + 363 + ### ✓ Defense-in-Depth 364 + 365 + The Weaver's Sigil complements existing AethelOS protections: 366 + 367 + | Protection | Defense Against | Status | 368 + |------------|----------------|--------| 369 + | **W^X** | Code injection | ✅ Active | 370 + | **ASLR** | Return-to-libc, ROP | ✅ Active | 371 + | **Capability Sealing** | Capability forgery | ✅ Active | 372 + | **Opaque Handles** | Direct capability manipulation | ✅ Active | 373 + | **Weaver's Sigil** | Stack buffer overflows | 🚧 Planned | 374 + 375 + ### ✓ Entropy Quality 376 + 377 + - **Per-Thread:** Each thread has unique canary (64-bit) 378 + - **Source:** ChaCha8Rng seeded from RDTSC 379 + - **Renewal:** New canary on every thread creation 380 + - **Secret:** Never exposed to userspace 381 + 382 + ### ✓ Performance 383 + 384 + - **LLVM Mode:** Negligible overhead (~1-3% for "strong" mode) 385 + - **Manual Mode:** Only protected functions pay cost 386 + - **Context Switch:** Single write to `__stack_chk_guard` (fast) 387 + 388 + ### ⚠ Limitations 389 + 390 + **Not Defeated By:** 391 + - Blind overwrites (canary is random) 392 + - Partial overwrites (full 8 bytes checked) 393 + - Thread confusion (per-thread canaries) 394 + 395 + **Can Be Bypassed If:** 396 + - Attacker can read canary (info leak) → Mitigated by ASLR + secret storage 397 + - Attacker can write to arbitrary memory (skips canary check) → W^X prevents code injection 398 + - Attacker can corrupt heap metadata (not stack) → Future: heap canaries 399 + 400 + --- 401 + 402 + ## Testing Strategy 403 + 404 + ### Unit Tests 405 + 406 + ```rust 407 + #[cfg(test)] 408 + mod tests { 409 + use super::*; 410 + 411 + #[test] 412 + fn test_canary_generation() { 413 + let thread1 = Thread::new(test_fn, ThreadPriority::Normal); 414 + let thread2 = Thread::new(test_fn, ThreadPriority::Normal); 415 + 416 + // Each thread should have unique sigil 417 + assert_ne!(thread1.sigil, thread2.sigil); 418 + 419 + // Sigils should be non-zero 420 + assert_ne!(thread1.sigil, 0); 421 + assert_ne!(thread2.sigil, 0); 422 + } 423 + 424 + #[test] 425 + #[should_panic(expected = "Stack canary violation")] 426 + fn test_canary_detection() { 427 + // Simulate stack overflow by corrupting __stack_chk_guard 428 + unsafe { 429 + let original = __stack_chk_guard; 430 + __stack_chk_guard = 0xDEADBEEF; // Corrupt! 431 + __stack_chk_fail(); // Should panic 432 + } 433 + } 434 + } 435 + ``` 436 + 437 + ### Integration Tests 438 + 439 + ```rust 440 + // In tests/stack_overflow_test.rs 441 + 442 + #[test_case] 443 + fn test_stack_overflow_detection() { 444 + // Create function with buffer overflow vulnerability 445 + #[stack_protected] 446 + fn vulnerable_function() { 447 + let mut buffer = [0u8; 16]; 448 + 449 + // Simulate overflow (writes past buffer end) 450 + unsafe { 451 + core::ptr::write_bytes(buffer.as_mut_ptr(), 0xFF, 64); 452 + } 453 + 454 + // Should panic before reaching here 455 + } 456 + 457 + // Test should catch panic 458 + let result = panic::catch_unwind(|| vulnerable_function()); 459 + assert!(result.is_err()); 460 + } 461 + ``` 462 + 463 + --- 464 + 465 + ## Implementation Phases 466 + 467 + ### Phase 1: Infrastructure (Week 1) 468 + - [ ] Add `sigil` field to `Thread` struct 469 + - [ ] Implement canary generation in `Thread::new()` 470 + - [ ] Add `get_current_sigil()` function 471 + - [ ] Write unit tests 472 + 473 + ### Phase 2: LLVM Integration (Week 1-2) 474 + - [ ] Update `x86_64-aethelos.json` with `"stack-protector": "strong"` 475 + - [ ] Implement `__stack_chk_fail()` handler 476 + - [ ] Implement `__stack_chk_guard` global 477 + - [ ] Add `init_thread_canary()` function 478 + - [ ] Test compilation with canaries enabled 479 + 480 + ### Phase 3: Scheduler Integration (Week 2) 481 + - [ ] Update context switch to set `__stack_chk_guard` 482 + - [ ] Update thread creation to initialize canary 483 + - [ ] Add boot-time canary initialization 484 + - [ ] Test thread switching preserves canaries 485 + 486 + ### Phase 4: Validation & Monitoring (Week 2-3) 487 + - [ ] Add Weaver's Sigil status to `wards` command 488 + - [ ] Implement canary violation logging 489 + - [ ] Add debug command to inspect thread sigils 490 + - [ ] Performance benchmarks 491 + 492 + ### Phase 5: Documentation (Week 3) 493 + - [ ] Update DESIGN.md with Weaver's Sigil section 494 + - [ ] Update CLAUDE.md with canary guidelines 495 + - [ ] Add security audit checklist 496 + - [ ] Create attack simulation tests 497 + 498 + --- 499 + 500 + ## Eldarin Shell Integration 501 + 502 + ### New Command: `sigils` 503 + 504 + Display active Weaver's Sigils for debugging: 505 + 506 + ``` 507 + ◈ The Weaver's Sigils - Stack Canary Protection 508 + 509 + Mode: LLVM Strong (all functions with buffers) 510 + Status: ✓ ACTIVE 511 + 512 + Active Sigils: 513 + Thread #0 (Boot): 0xDEADBEEF_CAFEBABE 514 + Thread #1 (Shell): 0x12345678_9ABCDEF0 515 + Thread #2 (Worker): 0xFEDCBA98_76543210 516 + Thread #3 (Idle): 0xABCDEF01_23456789 517 + 518 + Violations Detected: 0 (since boot) 519 + Last Violation: None 520 + 521 + The sigils remain pure. The threads weave in harmony. 522 + ``` 523 + 524 + ### Update `wards` Command 525 + 526 + Add Weaver's Sigil to security status: 527 + 528 + ```rust 529 + // In wards_command.rs 530 + 531 + pub fn show_wards_page(page: usize) { 532 + match page { 533 + 0 => { 534 + // ... existing W^X, ASLR output ... 535 + 536 + // Weaver's Sigil Status 537 + crate::println!(" Stack Canary Protection (Weaver's Sigil): ✓ Active"); 538 + crate::println!(" Mode: LLVM Strong (all functions with buffers)"); 539 + crate::println!(" Violations detected: 0"); 540 + crate::println!(); 541 + 542 + // ... rest of page ... 543 + } 544 + } 545 + } 546 + ``` 547 + 548 + --- 549 + 550 + ## Future Enhancements 551 + 552 + ### Heap Canaries 553 + 554 + Extend sigil concept to heap allocations: 555 + 556 + ```rust 557 + pub struct HeapObject { 558 + size: usize, 559 + sigil_prefix: u64, // Before allocation 560 + data: [u8], 561 + sigil_suffix: u64, // After allocation 562 + } 563 + ``` 564 + 565 + ### Sigil Renewal 566 + 567 + Periodically refresh canaries to limit attack windows: 568 + 569 + ```rust 570 + pub fn renew_sigil(thread: &mut Thread) { 571 + thread.sigil = generate_new_canary(); 572 + unsafe { 573 + if thread.id == current_thread_id() { 574 + init_thread_canary(thread.sigil); 575 + } 576 + } 577 + } 578 + ``` 579 + 580 + ### Safe Stack 581 + 582 + Separate safety-critical stack data (return addresses, saved registers) from unsafe data (buffers): 583 + 584 + ``` 585 + ┌─────────────────┐ 586 + │ Safe Stack │ ← Return addresses, canaries 587 + │ (Protected) │ 588 + ├─────────────────┤ 589 + │ Unsafe Stack │ ← Buffers, local variables 590 + │ (Unprotected) │ 591 + └─────────────────┘ 592 + ``` 593 + 594 + --- 595 + 596 + ## References 597 + 598 + - **PaX STACKLEAK:** https://pax.grsecurity.net/docs/stackleak.txt 599 + - **GCC Stack Protector:** https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html 600 + - **LLVM SafeStack:** https://clang.llvm.org/docs/SafeStack.html 601 + - **Microsoft Control Flow Guard:** https://docs.microsoft.com/en-us/windows/win32/secbp/control-flow-guard 602 + 603 + --- 604 + 605 + *"The Weaver's Sigil is not merely a guard—it is a promise. A promise that each thread's fate remains its own, untainted by the chaos of corruption."* 606 + 607 + **Status:** 🚧 Design Complete, Implementation Pending 608 + **Priority:** High (Security Critical) 609 + **Estimated Effort:** 2-3 weeks 610 + **Dependencies:** Existing entropy infrastructure (ChaCha8Rng)
+56 -54
heartwood/src/drivers/ata.rs
··· 60 60 /// Detect and initialize primary master drive (drive 0) 61 61 /// Based on r3 kernel's ATA driver: https://github.com/Narasimha1997/r3 62 62 pub fn detect_primary_master() -> Option<Self> { 63 - Self::detect_drive(ATA_PRIMARY_BASE, 0) 63 + unsafe { Self::detect_drive(ATA_PRIMARY_BASE, 0) } 64 64 } 65 65 66 66 /// Detect and initialize primary slave drive (drive 1) 67 67 pub fn detect_primary_slave() -> Option<Self> { 68 - Self::detect_drive(ATA_PRIMARY_BASE, 1) 68 + unsafe { Self::detect_drive(ATA_PRIMARY_BASE, 1) } 69 69 } 70 70 71 71 /// Internal function to detect a drive on a specific bus and drive number 72 - fn detect_drive(bus: u16, drive: u8) -> Option<Self> { 73 - 74 - unsafe { 75 - // Serial marker: 'A' = Starting detection 76 - Self::debug_char(b'A'); 72 + /// 73 + /// SAFETY: Performs raw I/O port access to detect and initialize ATA drives. 74 + unsafe fn detect_drive(bus: u16, drive: u8) -> Option<Self> { 75 + // Serial marker: 'A' = Starting detection 76 + Self::debug_char(b'A'); 77 77 78 - // Step 0: Disable interrupts on ATA controller 79 - // Write to Device Control Register (0x3F6): set nIEN bit (bit 1) 80 - outb(0x3F6, 0x02); // Disable interrupts 78 + // Step 0: Disable interrupts on ATA controller 79 + // Write to Device Control Register (0x3F6): set nIEN bit (bit 1) 80 + outb(0x3F6, 0x02); // Disable interrupts 81 81 82 - // Step 1: Select drive (0xA0 = master, 0xB0 = slave) 83 - let drive_select = 0xA0 | ((drive & 1) << 4); 84 - outb(bus + ATA_REG_DRIVE, drive_select); 82 + // Step 1: Select drive (0xA0 = master, 0xB0 = slave) 83 + let drive_select = 0xA0 | ((drive & 1) << 4); 84 + outb(bus + ATA_REG_DRIVE, drive_select); 85 85 86 86 // Step 2: Wait 400ns for drive selection to settle 87 87 for _ in 0..4 { ··· 147 147 148 148 // Check for error bit 149 149 if (status & ATA_STATUS_ERR) != 0 { 150 + let error = inb(bus + ATA_REG_ERROR); 150 151 Self::debug_char(b'!'); // Error 151 152 Self::debug_hex(status); 153 + Self::debug_char(b'E'); // Error register 154 + Self::debug_hex(error); 152 155 return None; 153 156 } 154 157 ··· 194 197 // If sector count is 0, use a default 195 198 let sectors = if sectors > 0 { sectors } else { 2048 }; 196 199 197 - // Serial marker: 'Z' = Success! 198 - Self::debug_char(b'Z'); 200 + // Serial marker: 'Z' = Success! 201 + Self::debug_char(b'Z'); 199 202 200 - Some(AtaDrive { 201 - bus, 202 - drive, 203 - sectors, 204 - sector_size: 512, 205 - }) 206 - } 203 + Some(AtaDrive { 204 + bus, 205 + drive, 206 + sectors, 207 + sector_size: 512, 208 + }) 207 209 } 208 210 209 211 /// Write a debug string to COM1 serial port 212 + /// 213 + /// SAFETY: Must be called from unsafe context. Performs raw I/O port access. 210 214 fn debug_str(s: &[u8]) { 211 - unsafe { 212 - for &byte in s { 213 - while (inb(0x3FD) & 0x20) == 0 {} 214 - outb(0x3F8, byte); 215 - } 215 + for &byte in s { 216 + while (inb(0x3FD) & 0x20) == 0 {} 217 + outb(0x3F8, byte); 216 218 } 217 219 } 218 220 219 221 /// Write a debug character to COM1 serial port with compact prefix 222 + /// 223 + /// SAFETY: Must be called from unsafe context. Performs raw I/O port access. 220 224 fn debug_char(c: u8) { 221 225 // Use compact format: "*X " - write directly without waiting 222 226 // to avoid potential conflicts with ATA port access 223 - unsafe { 224 - outb(0x3F8, b'*'); 225 - outb(0x3F8, c); 226 - outb(0x3F8, b' '); 227 - } 227 + outb(0x3F8, b'*'); 228 + outb(0x3F8, c); 229 + outb(0x3F8, b' '); 228 230 } 229 231 230 232 /// Write a byte as two hex digits to serial port 233 + /// 234 + /// SAFETY: Must be called from unsafe context. Performs raw I/O port access. 231 235 fn debug_hex(value: u8) { 232 236 const HEX: &[u8] = b"0123456789ABCDEF"; 233 - unsafe { 234 - let high = (value >> 4) & 0x0F; 235 - let low = value & 0x0F; 237 + let high = (value >> 4) & 0x0F; 238 + let low = value & 0x0F; 236 239 237 - // Write directly without waiting 238 - outb(0x3F8, b'='); 239 - outb(0x3F8, b'0'); 240 - outb(0x3F8, b'x'); 241 - outb(0x3F8, HEX[high as usize]); 242 - outb(0x3F8, HEX[low as usize]); 243 - } 240 + // Write directly without waiting 241 + outb(0x3F8, b'='); 242 + outb(0x3F8, b'0'); 243 + outb(0x3F8, b'x'); 244 + outb(0x3F8, HEX[high as usize]); 245 + outb(0x3F8, HEX[low as usize]); 244 246 } 245 247 246 248 /// Send IDENTIFY command to drive ··· 344 346 /// Read a single sector (28-bit LBA) 345 347 /// Read a single sector using PIO mode 346 348 /// Based on r3 kernel's read_sectors_lba28() 347 - fn read_sector_pio(&self, lba: u64) -> Result<Vec<u8>, BlockDeviceError> { 349 + /// 350 + /// SAFETY: Performs raw I/O port access to read sectors from ATA drive. 351 + unsafe fn read_sector_pio(&self, lba: u64) -> Result<Vec<u8>, BlockDeviceError> { 348 352 if lba >= self.sectors { 349 353 return Err(BlockDeviceError::InvalidSector); 350 354 } 351 355 352 - unsafe { 353 - // Wait for drive to not be busy 354 - let mut timeout = 0; 356 + // Wait for drive to not be busy 357 + let mut timeout = 0; 355 358 loop { 356 359 let status = inb(self.bus + ATA_REG_STATUS); 357 360 if status & ATA_STATUS_BSY == 0 { ··· 419 422 } 420 423 421 424 // Convert to Vec<u8> 422 - let mut buffer = Vec::with_capacity(512); 423 - for word in data.iter() { 424 - buffer.push((word & 0xFF) as u8); 425 - buffer.push((word >> 8) as u8); 426 - } 425 + let mut buffer = Vec::with_capacity(512); 426 + for word in data.iter() { 427 + buffer.push((word & 0xFF) as u8); 428 + buffer.push((word >> 8) as u8); 429 + } 427 430 428 - Ok(buffer) 429 - } 431 + Ok(buffer) 430 432 } 431 433 432 434 /// Wait for drive to not be busy (with timeout) ··· 466 468 } 467 469 468 470 fn read_sector(&self, sector: u64) -> Result<Vec<u8>, BlockDeviceError> { 469 - self.read_sector_pio(sector) 471 + unsafe { self.read_sector_pio(sector) } 470 472 } 471 473 472 474 fn read_sectors(&self, start_sector: u64, count: u32) -> Result<Vec<u8>, BlockDeviceError> {
+12 -4
heartwood/src/eldarin.rs
··· 457 457 "preempt" => cmd_preempt(args), 458 458 "uptime" => cmd_uptime(), 459 459 "wards" => cmd_wards(), // Security wards (ASLR, W^X) 460 + "sigils" => cmd_sigils(), // Weaver's Sigils (stack canaries) 460 461 // Filesystem commands (Eldarin naming) 461 462 "reveal" => cmd_vfs_ls(args), // vfs-ls → reveal 462 463 "recite" => cmd_vfs_cat(args), // vfs-cat → recite ··· 907 908 // Test 1: Check filesystem info 908 909 crate::println!(); 909 910 crate::println!(" 3. Testing filesystem metadata..."); 910 - crate::println!(" Volume label: {}", fat32.bpb.volume_label); 911 - crate::println!(" Filesystem: {}", fat32.bpb.fs_type); 911 + crate::println!(" Volume label: {}", 912 + core::str::from_utf8(&fat32.bpb.volume_label).unwrap_or("INVALID").trim_end()); 913 + crate::println!(" Filesystem: {}", 914 + core::str::from_utf8(&fat32.bpb.fs_type).unwrap_or("INVALID").trim_end()); 912 915 crate::println!(" Bytes per sector: {}", fat32.bpb.bytes_per_sector); 913 916 crate::println!(" Cluster size: {} bytes", fat32.bpb.cluster_size()); 914 917 ··· 984 987 985 988 /// VFS LS - List directory contents 986 989 fn cmd_vfs_ls(args: &str) { 987 - use crate::vfs::{FileSystem, Path}; 990 + use crate::vfs::Path; 988 991 use crate::vfs::global as vfs_global; 989 992 990 993 let path = if args.is_empty() { "/" } else { args.trim() }; ··· 1034 1037 1035 1038 /// VFS CAT - Display file contents 1036 1039 fn cmd_vfs_cat(args: &str) { 1037 - use crate::vfs::{FileSystem, Path}; 1040 + use crate::vfs::Path; 1038 1041 use crate::vfs::global as vfs_global; 1039 1042 1040 1043 if args.is_empty() { ··· 1106 1109 // Delegate to the paged wards command with capability testing 1107 1110 crate::wards_command::cmd_wards(); 1108 1111 } 1112 + 1113 + /// SIGILS - Display The Weaver's Sigils (stack canaries) 1114 + fn cmd_sigils() { 1115 + crate::sigils_command::cmd_sigils(); 1116 + }
+2
heartwood/src/lib.rs
··· 44 44 pub mod rtl; // Runtime library with memcpy, etc. 45 45 pub mod eldarin; // The Eldarin Shell 46 46 pub mod wards_command; // Security wards command 47 + pub mod sigils_command; // Weaver's Sigils command 48 + pub mod stack_protection; // Stack canary runtime (LLVM support) 47 49 pub mod irq_safe_mutex; // Interrupt-safe mutex primitive 48 50 pub mod vfs; // Virtual File System layer 49 51 pub mod drivers; // Hardware device drivers
+8
heartwood/src/loom_of_fate/scheduler.rs
··· 229 229 let from_ctx_ptr = &mut self.threads[from_idx].context as *mut ThreadContext; 230 230 let to_ctx_ptr = &self.threads[to_idx].context as *const ThreadContext; 231 231 232 + // Update The Weaver's Sigil (stack canary) for the new thread 233 + // SECURITY: This MUST happen before the context switch so that 234 + // LLVM-generated code in the new thread uses the correct canary 235 + let next_sigil = self.threads[to_idx].sigil; 236 + unsafe { 237 + crate::stack_protection::set_current_canary(next_sigil); 238 + } 239 + 232 240 // Update current thread ID 233 241 self.current_thread = Some(next_id); 234 242 self.context_switches += 1;
+26
heartwood/src/loom_of_fate/thread.rs
··· 51 51 #[allow(dead_code)] 52 52 pub(crate) stack_top: u64, 53 53 54 + /// The Weaver's Sigil - unique per-thread stack canary 55 + /// This secret value protects against stack buffer overflows 56 + /// It should NEVER be exposed to userspace 57 + pub(crate) sigil: u64, 58 + 54 59 // Harmony tracking 55 60 pub(crate) resource_usage: ResourceUsage, 56 61 pub(crate) harmony_score: f32, ··· 80 85 // Create initial context for this thread 81 86 let context = ThreadContext::new(entry_point as u64, stack_top); 82 87 88 + // Generate unique Weaver's Sigil (stack canary) for this thread 89 + let sigil = Self::generate_sigil(); 90 + 83 91 // DEBUG: Verify context was created correctly 84 92 crate::println!(" Thread::new - id={}, entry={:#x}, stack_top={:#x}, context.rsp={:#x}", 85 93 id.0, entry_point as u64, stack_top, context.rsp); 94 + crate::println!(" Weaver's Sigil: 0x{:016x}", sigil); 86 95 87 96 Self { 88 97 id, ··· 92 101 context, 93 102 stack_bottom, 94 103 stack_top, 104 + sigil, 95 105 resource_usage: ResourceUsage::default(), 96 106 harmony_score: 1.0, // Start in perfect harmony 97 107 time_slices_used: 0, 98 108 yields: 0, 99 109 last_run_time: 0, 100 110 } 111 + } 112 + 113 + /// Generate a unique Weaver's Sigil (stack canary) for this thread 114 + /// 115 + /// Uses ChaCha8 RNG seeded from hardware (RDTSC) to generate 116 + /// a cryptographically strong 64-bit random value. 117 + /// 118 + /// # Security 119 + /// This value MUST remain secret and never be exposed to userspace. 120 + fn generate_sigil() -> u64 { 121 + use crate::mana_pool::entropy::ChaCha8Rng; 122 + 123 + let mut rng = ChaCha8Rng::from_hardware_fast(); 124 + let high = rng.next_u32() as u64; 125 + let low = rng.next_u32() as u64; 126 + (high << 32) | low 101 127 } 102 128 103 129 /// Get a mutable reference to the thread's context
+19
heartwood/src/main.rs
··· 193 193 unsafe { mana_pool::sealing::init(); } 194 194 println!(" ✓ Capability sealing ready (HMAC-SHA256)"); 195 195 196 + // Initialize The Weaver's Sigil (stack canary protection) 197 + println!("◈ Weaving the protective sigils..."); 198 + unsafe { 199 + // Set initial canary for boot thread 200 + // This will be updated per-thread by the scheduler during context switches 201 + use mana_pool::entropy::ChaCha8Rng; 202 + let mut rng = ChaCha8Rng::from_hardware_fast(); 203 + let boot_sigil = ((rng.next_u32() as u64) << 32) | (rng.next_u32() as u64); 204 + heartwood::stack_protection::set_current_canary(boot_sigil); 205 + } 206 + println!(" ✓ The Weaver's Sigil active (stack canary: LLVM strong mode)"); 207 + println!(" Protecting all functions with buffers or address-taken locals"); 208 + 196 209 // Initialize the Nexus (IPC) 197 210 unsafe { serial_out(b'E'); } 198 211 println!("◈ Opening the Nexus..."); ··· 206 219 loom_of_fate::init(); 207 220 unsafe { serial_out(b'H'); } 208 221 println!(" ✓ Loom ready"); 222 + 223 + // Initialize heap canaries (after thread creation to avoid early boot issues) 224 + unsafe { 225 + mana_pool::heap_canaries::init(); 226 + } 227 + println!(" ✓ Heap canaries active (pre/post allocation protection)"); 209 228 210 229 // Initialize the Attunement Layer 211 230 unsafe { serial_out(b'I'); }
+63 -8
heartwood/src/mana_pool/buddy.rs
··· 120 120 121 121 /// Allocate a block of at least `size` bytes 122 122 /// 123 - /// Returns the address of the allocated block, or None if out of memory 123 + /// Returns the address of the allocated block (user data, after pre-canary), 124 + /// or None if out of memory. 125 + /// 126 + /// Memory layout: 127 + /// ``` 128 + /// [PRE_CANARY (8B)] [USER DATA (size)] [POST_CANARY (8B)] 129 + /// ^ ^ 130 + /// block address returned address 131 + /// ``` 124 132 pub fn allocate(&mut self, size: usize) -> Option<usize> { 125 133 if size == 0 { 126 134 return None; 127 135 } 128 136 137 + // Check if heap canaries are enabled 138 + let canaries_enabled = super::heap_canaries::are_enabled(); 139 + 140 + // Add space for heap canaries only if enabled 141 + let total_size = if canaries_enabled { 142 + size + super::heap_canaries::TOTAL_CANARY_OVERHEAD 143 + } else { 144 + size 145 + }; 146 + 129 147 // Find the order needed for this size 130 - let needed_order = self.size_to_order(size); 148 + let needed_order = self.size_to_order(total_size); 131 149 if needed_order > MAX_ORDER { 132 150 return None; // Allocation too large 133 151 } ··· 141 159 // Split block if it's larger than needed 142 160 self.split_block(block, alloc_order, needed_order); 143 161 144 - Some(block.as_ptr() as usize) 162 + let block_addr = block.as_ptr() as usize; 163 + 164 + // Write heap canaries around the allocation (only if enabled) 165 + if canaries_enabled { 166 + unsafe { 167 + super::heap_canaries::write_canaries(block_addr, size); 168 + } 169 + // Return pointer to user data (after pre-canary) 170 + Some(block_addr + super::heap_canaries::CANARY_SIZE) 171 + } else { 172 + // Return block address directly (no canary offset) 173 + Some(block_addr) 174 + } 145 175 } 146 176 147 177 /// Deallocate a block ··· 149 179 /// # Safety 150 180 /// 151 181 /// The caller must ensure: 152 - /// - `addr` was returned by a previous call to `allocate` 153 - /// - `size` matches the size passed to `allocate` 182 + /// - `addr` was returned by a previous call to `allocate` (points to user data) 183 + /// - `size` matches the size passed to `allocate` (user data size, not including canaries) 154 184 /// - The block has not already been deallocated 155 185 pub unsafe fn deallocate(&mut self, addr: usize, size: usize) { 156 186 if size == 0 { 157 187 return; 158 188 } 159 189 160 - let order = self.size_to_order(size); 190 + // Check if heap canaries are enabled 191 + let canaries_enabled = super::heap_canaries::are_enabled(); 192 + 193 + let (block_addr, total_size) = if canaries_enabled { 194 + // Calculate the actual block address (before pre-canary) 195 + let blk_addr = addr - super::heap_canaries::CANARY_SIZE; 196 + 197 + // Verify heap canaries before deallocation 198 + if !super::heap_canaries::verify_canaries(blk_addr, size) { 199 + panic!("◈ HEAP CORRUPTION: Canary violation detected during deallocation!\n\ 200 + \n\ 201 + Address: 0x{:016x}\n\ 202 + Size: {} bytes\n\ 203 + \n\ 204 + The Weaver's Sigil has detected heap buffer overflow.\n\ 205 + This allocation's protective canaries were corrupted.", addr, size); 206 + } 207 + 208 + // Calculate total size including canaries 209 + (blk_addr, size + super::heap_canaries::TOTAL_CANARY_OVERHEAD) 210 + } else { 211 + // No canaries - addr is the block address, size is actual size 212 + (addr, size) 213 + }; 214 + 215 + let order = self.size_to_order(total_size); 161 216 if order > MAX_ORDER { 162 217 return; 163 218 } 164 219 165 - // Create a block at this address 166 - let block = Block::new(addr); 220 + // Create a block at the actual block address 221 + let block = Block::new(block_addr); 167 222 168 223 // Try to coalesce with buddy 169 224 self.coalesce_and_free(block, order);
+138
heartwood/src/mana_pool/heap_canaries.rs
··· 1 + //! Heap Canaries - The Weaver's Sigil for Heap Protection 2 + //! 3 + //! Protects heap allocations from buffer overflows by placing canary values 4 + //! before and after each allocation. If a buffer overflow occurs, it will 5 + //! corrupt a canary, which is detected when the memory is freed. 6 + //! 7 + //! ## Philosophy 8 + //! Just as threads are protected by the Weaver's Sigil on the stack, 9 + //! heap allocations are guarded by sigils woven into the fabric of memory. 10 + //! Each allocation is wrapped in protective marks that reveal corruption. 11 + 12 + use crate::mana_pool::entropy::ChaCha8Rng; 13 + use core::sync::atomic::{AtomicU64, Ordering}; 14 + 15 + /// Size of each canary (8 bytes) 16 + pub const CANARY_SIZE: usize = 8; 17 + 18 + /// Total overhead per allocation (pre-canary + post-canary) 19 + pub const TOTAL_CANARY_OVERHEAD: usize = CANARY_SIZE * 2; 20 + 21 + /// Global canary secret (XORed with allocation address for uniqueness) 22 + static CANARY_SECRET: AtomicU64 = AtomicU64::new(0); 23 + 24 + /// Statistics for heap canary violations 25 + static VIOLATIONS_DETECTED: AtomicU64 = AtomicU64::new(0); 26 + 27 + /// Enable/disable heap canary checking 28 + static CANARIES_ENABLED: AtomicU64 = AtomicU64::new(0); 29 + 30 + /// Check if heap canaries are currently enabled 31 + #[inline(always)] 32 + pub fn are_enabled() -> bool { 33 + CANARIES_ENABLED.load(Ordering::Acquire) != 0 34 + } 35 + 36 + /// Initialize heap canary system 37 + /// 38 + /// # Safety 39 + /// Must be called exactly once during kernel initialization 40 + pub unsafe fn init() { 41 + // Generate a random canary secret 42 + let mut rng = ChaCha8Rng::from_hardware_fast(); 43 + let secret = ((rng.next_u32() as u64) << 32) | (rng.next_u32() as u64); 44 + CANARY_SECRET.store(secret, Ordering::Relaxed); 45 + 46 + // Enable heap canaries 47 + CANARIES_ENABLED.store(1, Ordering::Release); 48 + } 49 + 50 + /// Generate a canary value for a specific allocation address 51 + /// 52 + /// The canary is unique per-allocation by XORing the secret with the address. 53 + /// This prevents an attacker from learning the canary from one allocation 54 + /// and using it to bypass protection in another. 55 + #[inline(always)] 56 + pub fn generate_canary(addr: usize) -> u64 { 57 + let secret = CANARY_SECRET.load(Ordering::Relaxed); 58 + secret ^ (addr as u64) 59 + } 60 + 61 + /// Write canaries around an allocation 62 + /// 63 + /// Memory layout: 64 + /// ``` 65 + /// [PRE_CANARY][USER DATA][POST_CANARY] 66 + /// 8 bytes size bytes 8 bytes 67 + /// ``` 68 + /// 69 + /// # Safety 70 + /// - `addr` must point to valid memory with at least `size + TOTAL_CANARY_OVERHEAD` bytes 71 + /// - Memory must be writable 72 + pub unsafe fn write_canaries(addr: usize, size: usize) { 73 + let canary = generate_canary(addr); 74 + 75 + // Write pre-canary before user data 76 + let pre_canary_ptr = addr as *mut u64; 77 + core::ptr::write_volatile(pre_canary_ptr, canary); 78 + 79 + // Write post-canary after user data 80 + let post_canary_ptr = (addr + CANARY_SIZE + size) as *mut u64; 81 + core::ptr::write_volatile(post_canary_ptr, canary); 82 + } 83 + 84 + /// Verify canaries and detect heap corruption 85 + /// 86 + /// Returns true if canaries are intact, false if corrupted 87 + /// 88 + /// # Safety 89 + /// - `addr` must point to the start of an allocation (including pre-canary) 90 + /// - Memory must be readable 91 + pub unsafe fn verify_canaries(addr: usize, size: usize) -> bool { 92 + // Skip verification if canaries not enabled yet (during early boot) 93 + if CANARIES_ENABLED.load(Ordering::Acquire) == 0 { 94 + return true; 95 + } 96 + 97 + let expected_canary = generate_canary(addr); 98 + 99 + // Check pre-canary 100 + let pre_canary_ptr = addr as *const u64; 101 + let pre_canary = core::ptr::read_volatile(pre_canary_ptr); 102 + 103 + // Check post-canary 104 + let post_canary_ptr = (addr + CANARY_SIZE + size) as *const u64; 105 + let post_canary = core::ptr::read_volatile(post_canary_ptr); 106 + 107 + if pre_canary != expected_canary { 108 + log_violation(addr, size, "PRE-CANARY", expected_canary, pre_canary); 109 + return false; 110 + } 111 + 112 + if post_canary != expected_canary { 113 + log_violation(addr, size, "POST-CANARY", expected_canary, post_canary); 114 + return false; 115 + } 116 + 117 + true 118 + } 119 + 120 + /// Log a canary violation 121 + unsafe fn log_violation(_addr: usize, _size: usize, _location: &str, _expected: u64, _found: u64) { 122 + // Track violation count 123 + // Detailed logging is done in the panic message when deallocation fails 124 + VIOLATIONS_DETECTED.fetch_add(1, Ordering::Relaxed); 125 + } 126 + 127 + /// Get the number of heap canary violations detected since boot 128 + pub fn violations_count() -> u64 { 129 + VIOLATIONS_DETECTED.load(Ordering::Relaxed) 130 + } 131 + 132 + /// Get the current canary secret (for debugging only) 133 + /// 134 + /// # Security Warning 135 + /// This should NEVER be exposed to userspace! 136 + pub fn get_canary_secret() -> u64 { 137 + CANARY_SECRET.load(Ordering::Relaxed) 138 + }
+1
heartwood/src/mana_pool/mod.rs
··· 26 26 pub mod entropy; // Random number generation for ASLR 27 27 pub mod aslr; // Address Space Layout Randomization 28 28 pub mod sealing; // Cryptographic capability sealing 29 + pub mod heap_canaries; // Heap buffer overflow protection 29 30 30 31 pub use object_manager::{ObjectManager, ObjectHandle, ObjectType, ObjectInfo}; 31 32 pub use capability::{Capability, CapabilityRights, CapabilityId, SealedCapability};
+1 -1
heartwood/src/mana_pool/sealing.rs
··· 11 11 ///! - **Kernel-Only**: Secret key never leaves kernel space 12 12 13 13 use core::mem::MaybeUninit; 14 - use crate::mana_pool::entropy::{HardwareRng, ChaCha8Rng}; 14 + use crate::mana_pool::entropy::ChaCha8Rng; 15 15 16 16 /// Capability sealer using HMAC-SHA256 17 17 pub struct CapabilitySealer {
+105
heartwood/src/sigils_command.rs
··· 1 + /// SIGILS - Display The Weaver's Sigils (stack canaries) for all threads 2 + /// 3 + /// This command shows the unique stack canary values protecting each thread. 4 + /// Each sigil is a 64-bit cryptographically random value that guards against 5 + /// buffer overflow attacks. 6 + 7 + pub fn cmd_sigils() { 8 + use crate::loom_of_fate::{without_interrupts, ThreadState}; 9 + use alloc::vec::Vec; 10 + 11 + crate::println!("◈ The Weaver's Sigils - Stack Canary Protection"); 12 + crate::println!(); 13 + 14 + crate::println!(" Status: ✓ ACTIVE (Phase 2: LLVM stack protection)"); 15 + crate::println!(" Mode: LLVM strong mode + per-thread canaries"); 16 + crate::println!(" Protection: All functions with buffers or address-taken locals"); 17 + crate::println!(); 18 + 19 + // Show current global canary value 20 + let current_canary = crate::stack_protection::get_current_canary(); 21 + crate::println!(" Current __stack_chk_guard: 0x{:016x}", current_canary); 22 + crate::println!(); 23 + 24 + // Get thread information 25 + without_interrupts(|| { 26 + unsafe { 27 + let loom = crate::loom_of_fate::get_loom().lock(); 28 + 29 + // Collect threads (not fading) 30 + let threads: Vec<_> = loom.threads.iter() 31 + .filter(|t| !matches!(t.state, ThreadState::Fading)) 32 + .collect(); 33 + 34 + crate::println!(" Active Sigils ({} threads):", threads.len()); 35 + crate::println!(); 36 + 37 + for thread in threads { 38 + let state_str = match thread.state { 39 + ThreadState::Weaving => "Weaving", 40 + ThreadState::Resting => "Resting", 41 + ThreadState::Tangled => "Tangled", 42 + ThreadState::Fading => "Fading", 43 + }; 44 + 45 + let priority_str = match thread.priority { 46 + crate::loom_of_fate::ThreadPriority::Critical => "Critical", 47 + crate::loom_of_fate::ThreadPriority::High => "High", 48 + crate::loom_of_fate::ThreadPriority::Normal => "Normal", 49 + crate::loom_of_fate::ThreadPriority::Low => "Low", 50 + crate::loom_of_fate::ThreadPriority::Idle => "Idle", 51 + }; 52 + 53 + crate::println!(" Thread #{} [{}|{}]", 54 + thread.id.0, state_str, priority_str); 55 + crate::println!(" Sigil: 0x{:016x}", thread.sigil); 56 + crate::println!(); 57 + } 58 + 59 + // Verify uniqueness 60 + let mut sigils: Vec<u64> = loom.threads.iter() 61 + .filter(|t| !matches!(t.state, ThreadState::Fading)) 62 + .map(|t| t.sigil) 63 + .collect(); 64 + 65 + let original_len = sigils.len(); 66 + sigils.sort_unstable(); 67 + sigils.dedup(); 68 + let unique_len = sigils.len(); 69 + 70 + if original_len == unique_len { 71 + crate::println!(" ✓ All sigils are unique ({} distinct values)", unique_len); 72 + } else { 73 + crate::println!(" ✗ WARNING: {} duplicate sigils detected!", 74 + original_len - unique_len); 75 + } 76 + 77 + crate::println!(); 78 + crate::println!(" Protection Status:"); 79 + crate::println!(" [✓] Per-thread storage - Active"); 80 + crate::println!(" [✓] Cryptographic generation - Active"); 81 + crate::println!(" [✓] Stack canary placement - Active (LLVM inserts at function entry)"); 82 + crate::println!(" [✓] Overflow detection - Active (LLVM checks before return)"); 83 + crate::println!(); 84 + crate::println!(" Phase 2 Complete: LLVM stack-protector (strong mode) is active."); 85 + crate::println!(" All functions with buffers or address-taken locals are protected."); 86 + } 87 + }); 88 + 89 + // Heap Canary Status 90 + crate::println!(); 91 + crate::println!(" Heap Protection (Buffer Overflow Detection):"); 92 + crate::println!(" [✓] Pre-allocation canaries - Active (8 bytes before each allocation)"); 93 + crate::println!(" [✓] Post-allocation canaries - Active (8 bytes after each allocation)"); 94 + 95 + let violations = crate::mana_pool::heap_canaries::violations_count(); 96 + if violations == 0 { 97 + crate::println!(" [✓] Violations detected - None (heap integrity maintained)"); 98 + } else { 99 + crate::println!(" [✗] Violations detected - {} (HEAP CORRUPTION!)", violations); 100 + } 101 + 102 + crate::println!(); 103 + crate::println!("The sigils remain pure and distinct."); 104 + crate::println!("Stack and heap are guarded by the Weaver's protective marks."); 105 + }
+162
heartwood/src/stack_protection.rs
··· 1 + //! Stack Protection Runtime Support 2 + //! 3 + //! This module provides the runtime support required by LLVM's stack protector. 4 + //! When `-fstack-protector` is enabled, LLVM generates code that: 5 + //! 1. Reads from `__stack_chk_guard` at function entry 6 + //! 2. Places the value on the stack (the canary) 7 + //! 3. Checks the value before function return 8 + //! 4. Calls `__stack_chk_fail()` if the canary was corrupted 9 + 10 + use core::sync::atomic::{AtomicU64, Ordering}; 11 + 12 + /// Global stack canary value (thread-local via context switch) 13 + /// 14 + /// LLVM-generated code reads this value at function entry and checks it 15 + /// at function exit. We update this value on every context switch to use 16 + /// the current thread's unique Weaver's Sigil. 17 + /// 18 + /// # Security 19 + /// This value changes per-thread, making it harder for attackers to 20 + /// predict the canary. However, an attacker with arbitrary read can 21 + /// still extract it, which is why we combine this with ASLR and other 22 + /// defenses. 23 + #[no_mangle] 24 + pub static __stack_chk_guard: AtomicU64 = AtomicU64::new(0xDEADBEEF_CAFEBABE); 25 + 26 + /// Stack canary failure handler 27 + /// 28 + /// This function is called by LLVM-generated code when a stack canary 29 + /// check fails, indicating that a buffer overflow has corrupted the stack. 30 + /// 31 + /// # Behavior 32 + /// This function NEVER returns. It logs diagnostic information and then 33 + /// panics to prevent the corrupted thread from continuing execution. 34 + /// 35 + /// # Safety 36 + /// This function is called by compiler-generated code in a potentially 37 + /// corrupted context. We must not trust any stack-allocated data and 38 + /// should minimize operations that might use corrupted state. 39 + #[no_mangle] 40 + pub extern "C" fn __stack_chk_fail() -> ! { 41 + // Log the violation (use serial port directly to avoid stack operations) 42 + unsafe { 43 + log_canary_violation(); 44 + } 45 + 46 + // Panic with a clear message 47 + panic!("◈ STACK CANARY VIOLATION: The Weaver's Sigil has been corrupted!\n\ 48 + \n\ 49 + A buffer overflow has been detected. The thread's stack canary\n\ 50 + was overwritten, indicating memory corruption. Execution cannot\n\ 51 + continue safely.\n\ 52 + \n\ 53 + This protection prevented the overflow from hijacking control flow.\n\ 54 + The Weaver's Sigil stands vigilant."); 55 + } 56 + 57 + /// Log diagnostic information about the canary violation 58 + /// 59 + /// This function is called from `__stack_chk_fail()` to provide debugging 60 + /// information about which thread detected the corruption. 61 + /// 62 + /// # Safety 63 + /// Must be called with interrupts disabled to avoid corruption of diagnostic 64 + /// output. Avoids using the stack as much as possible. 65 + unsafe fn log_canary_violation() { 66 + // Print to VGA (safer than serial which might use stack) 67 + crate::println!("\n╔════════════════════════════════════════════════════════╗"); 68 + crate::println!("║ ⚠ STACK CANARY VIOLATION DETECTED ⚠ ║"); 69 + crate::println!("╚════════════════════════════════════════════════════════╝"); 70 + crate::println!(); 71 + crate::println!(" The Weaver's Sigil has been corrupted!"); 72 + crate::println!(" A buffer overflow has overwritten the stack canary."); 73 + crate::println!(); 74 + 75 + // Try to get current thread info (might fail if stack is corrupted) 76 + // Note: We can't use catch_unwind in no_std, so if this fails, we'll panic anyway 77 + if let Some(thread_id) = crate::loom_of_fate::current_thread() { 78 + crate::println!(" Thread ID: {}", thread_id.0); 79 + } else { 80 + crate::println!(" Thread ID: <unable to determine>"); 81 + } 82 + 83 + crate::println!(); 84 + crate::println!(" Expected canary: 0x{:016x}", __stack_chk_guard.load(Ordering::Relaxed)); 85 + crate::println!(" Actual canary: <corrupted>"); 86 + crate::println!(); 87 + crate::println!(" This overflow was BLOCKED by The Weaver's Sigil."); 88 + crate::println!(" Execution halted before control flow hijacking."); 89 + crate::println!(); 90 + 91 + // Increment violation counter (if we have one in the future) 92 + // CANARY_VIOLATIONS.fetch_add(1, Ordering::Relaxed); 93 + } 94 + 95 + /// Initialize the stack canary for a thread 96 + /// 97 + /// This function should be called during context switch to update 98 + /// `__stack_chk_guard` with the current thread's unique sigil. 99 + /// 100 + /// # Arguments 101 + /// * `sigil` - The thread's unique Weaver's Sigil (64-bit random value) 102 + /// 103 + /// # Safety 104 + /// This function must be called during context switch, with interrupts 105 + /// disabled, to avoid race conditions where LLVM-generated code reads 106 + /// a canary from one thread while executing another thread's code. 107 + #[inline(always)] 108 + pub unsafe fn set_current_canary(sigil: u64) { 109 + __stack_chk_guard.store(sigil, Ordering::Relaxed); 110 + } 111 + 112 + /// Get the current canary value 113 + /// 114 + /// This is primarily for debugging and testing. Production code should 115 + /// not need to read this value directly (LLVM-generated code handles it). 116 + /// 117 + /// # Returns 118 + /// The current value of `__stack_chk_guard` 119 + #[inline(always)] 120 + pub fn get_current_canary() -> u64 { 121 + __stack_chk_guard.load(Ordering::Relaxed) 122 + } 123 + 124 + /// Test helper: Simulate a stack canary violation 125 + /// 126 + /// This function is used in tests to verify that canary checking works. 127 + /// DO NOT call this in production code! 128 + #[cfg(test)] 129 + pub fn simulate_violation() -> ! { 130 + __stack_chk_fail() 131 + } 132 + 133 + #[cfg(test)] 134 + mod tests { 135 + use super::*; 136 + 137 + #[test] 138 + fn test_canary_get_set() { 139 + unsafe { 140 + set_current_canary(0x1234567890ABCDEF); 141 + } 142 + assert_eq!(get_current_canary(), 0x1234567890ABCDEF); 143 + 144 + unsafe { 145 + set_current_canary(0xFEDCBA0987654321); 146 + } 147 + assert_eq!(get_current_canary(), 0xFEDCBA0987654321); 148 + } 149 + 150 + #[test] 151 + fn test_canary_default() { 152 + // Default value should be non-zero (our placeholder) 153 + let default = get_current_canary(); 154 + assert_ne!(default, 0, "Canary should have non-zero default"); 155 + } 156 + 157 + #[test] 158 + #[should_panic(expected = "STACK CANARY VIOLATION")] 159 + fn test_stack_chk_fail_panics() { 160 + simulate_violation(); 161 + } 162 + }
+20 -14
heartwood/src/vfs/fat32/bpb.rs
··· 102 102 pub fsinfo_sector: u16, 103 103 /// Backup boot sector location (usually 6, 0xFFFF if not present) 104 104 pub backup_boot_sector: u16, 105 - /// Volume label (11 characters, space-padded) 106 - pub volume_label: String, 107 - /// Filesystem type string (should be "FAT32 ") 108 - pub fs_type: String, 105 + /// Volume label (11 bytes, space-padded) 106 + pub volume_label: [u8; 11], 107 + /// Filesystem type string (8 bytes, should be "FAT32 ") 108 + pub fs_type: [u8; 8], 109 109 /// Cached FSInfo data (if available) 110 110 pub fsinfo: Option<FSInfo>, 111 111 } ··· 183 183 // Backup boot sector at offset 50 (usually 6) 184 184 let backup_boot_sector = u16::from_le_bytes([boot_sector[50], boot_sector[51]]); 185 185 186 - // Volume label at offset 71 (11 bytes) 187 - let volume_label = core::str::from_utf8(&boot_sector[71..82]) 188 - .unwrap_or("INVALID ") 189 - .trim_end() 190 - .to_string(); 186 + // Volume label at offset 71 (11 bytes) - copy directly, no String allocation 187 + let mut volume_label = [0u8; 11]; 188 + volume_label.copy_from_slice(&boot_sector[71..82]); 191 189 192 190 // Filesystem type at offset 82 (8 bytes, should be "FAT32 ") 193 - let fs_type = core::str::from_utf8(&boot_sector[82..90]) 194 - .unwrap_or("UNKNOWN ") 195 - .trim_end() 196 - .to_string(); 191 + let mut fs_type = [0u8; 8]; 192 + fs_type.copy_from_slice(&boot_sector[82..90]); 197 193 198 194 // Validate it's actually FAT32 199 - if !fs_type.starts_with("FAT32") { 195 + if !fs_type.starts_with(b"FAT32") { 200 196 return Err("Filesystem type is not FAT32"); 201 197 } 202 198 ··· 285 281 /// 2. If that fails, try the backup boot sector (if specified) 286 282 /// 3. Parse FSInfo (if present and valid) 287 283 pub fn from_device(device: &dyn BlockDevice) -> Result<Self, &'static str> { 284 + // Debug: entering from_device 285 + unsafe { 286 + core::arch::asm!("out dx, al", in("dx") 0x3F8u16, in("al") b'A', options(nomem, nostack)); 287 + } 288 + 288 289 // Try primary boot sector first 289 290 let boot_sector_result = device.read_sector(0); 291 + 292 + // Debug: read_sector returned 293 + unsafe { 294 + core::arch::asm!("out dx, al", in("dx") 0x3F8u16, in("al") b'B', options(nomem, nostack)); 295 + } 290 296 291 297 let (boot_sector, used_backup) = match boot_sector_result { 292 298 Ok(data) => {
+10
heartwood/src/vfs/fat32/mod.rs
··· 57 57 /// * `Ok(Fat32)` - Successfully mounted FAT32 filesystem 58 58 /// * `Err(FsError)` - Invalid or corrupted filesystem 59 59 pub fn new(device: Box<dyn BlockDevice>) -> Result<Self, FsError> { 60 + // Debug: entering Fat32::new 61 + unsafe { 62 + core::arch::asm!("out dx, al", in("dx") 0x3F8u16, in("al") b'1', options(nomem, nostack)); 63 + } 64 + 60 65 let bpb = Fat32Bpb::from_device(&*device) 61 66 .map_err(|_| FsError::IoError)?; 67 + 68 + // Debug: BPB parsed successfully 69 + unsafe { 70 + core::arch::asm!("out dx, al", in("dx") 0x3F8u16, in("al") b'2', options(nomem, nostack)); 71 + } 62 72 63 73 Ok(Self { device, bpb }) 64 74 }
+2 -4
heartwood/src/wards_command.rs
··· 155 155 crate::println!(" ✓ Derived capability ID: {}", derived_id.raw()); 156 156 157 157 // Store for page 2 158 - unsafe { 159 - STORED_CAP_ID = Some(cap_id); 160 - STORED_DERIVED_ID = Some(derived_id); 161 - } 158 + STORED_CAP_ID = Some(cap_id); 159 + STORED_DERIVED_ID = Some(derived_id); 162 160 } 163 161 Err(e) => { 164 162 crate::println!(" ✗ Derivation failed: {:?}", e);
+2 -1
heartwood/x86_64-aethelos.json
··· 15 15 "code-model": "kernel", 16 16 "relocation-model": "static", 17 17 "position-independent-executables": false, 18 - "static-position-independent-executables": false 18 + "static-position-independent-executables": false, 19 + "supports-stack-protector": true 19 20 }
isodir/boot/aethelos/heartwood.bin

This is a binary file and will not be displayed.