// K1315 native EL0 attenuation probe over a real child process identity. // The File object is a kernel-minted probe name; this gate checks rights, // duplication and ownership, not disk writes. use super::*; use turkos_kernel::runtime::handles::{Error, Handle, Object, Rights, Table}; use turkos_kernel::runtime::process_table::{Outcome, ProcessId}; use turkos_kernel::runtime::syscall_abi as abi; static mut OS_K1315_HANDLES: Table = Table::new(); static mut OS_K1315_STAGE: u8 = 0; const ORIGINAL: u64 = (1u64 << 32) | 1; const COPY: u64 = (1u64 << 32) | 2; fn handles() -> &'static mut Table { // Mode 54 runs on one CPU with IRQs masked and no concurrent table user. unsafe { &mut *core::ptr::addr_of_mut!(OS_K1315_HANDLES) } } fn stage() -> u8 { unsafe { read_volatile(core::ptr::addr_of!(OS_K1315_STAGE)) } } fn set_stage(value: u8) { unsafe { write_volatile(core::ptr::addr_of_mut!(OS_K1315_STAGE), value); } } pub(super) fn begin() { unsafe { core::ptr::write(core::ptr::addr_of_mut!(OS_K1315_HANDLES), Table::new()); } set_stage(0); } pub(super) fn finish() -> Result<(), &'static str> { let snapshot = handles().snapshot(); if stage() != 13 || handles().len() != 0 || snapshot.generations != [1, 1, 0] || snapshot.entries != [None; 3] { return Err("rights handle owner leak"); } Ok(()) } pub(super) fn dispatch( episode: &mut OsK1215AEl0Episode, frame: &mut os_k920_exception::Frame, process_table: &mut OsK1105Table<'_>, ) -> u64 { if frame.elr < episode.launch.user_pc || frame.elr >= episode.launch.user_pc + 4096 || !frame.elr.is_multiple_of(4) { park(); } if frame.x[7] == 3 { if episode.case != 1 || episode.calls != 1 || process_table.record(episode.pid).is_err() { park(); } let owner = episode.pid; match frame.x[8] { 18 => { if stage() != 0 || frame.x[0] != 1 || handles().len() != 0 { park(); } let issued = handles().open(owner, Object::File(41)) .unwrap_or_else(|_| park()); if issued.raw() != ORIGINAL || handles().len() != 1 || handles().owned(owner) != 1 { park(); } frame.x[0] = issued.raw(); set_stage(1); write_line("TURKOS/OS/K1315R-TRACE: OPEN-FILE-RW"); 0 } 25 => { if stage() == 1 && frame.x[0] == ORIGINAL && frame.x[1] == 1 { let before = handles().snapshot(); let copy = handles().duplicate(owner, Handle::from_raw(ORIGINAL), Rights::READ) .unwrap_or_else(|_| park()); if copy.raw() != COPY || handles().len() != 2 || handles().owned(owner) != 2 || handles().snapshot().entries[0] != before.entries[0] { park(); } frame.x[0] = copy.raw(); set_stage(2); write_line("TURKOS/OS/K1315R-TRACE: DUPLICATE-RW-TO-RO-TWO-OWNED"); } else if stage() == 6 && frame.x[0] == COPY && frame.x[1] == 3 { let before = handles().snapshot(); if handles().duplicate(owner, Handle::from_raw(COPY), Rights::READ_WRITE) != Err(Error::Access) || handles().snapshot() != before { park(); } frame.x[0] = abi::AbiResult::Err(abi::Errno::Access).encode(); set_stage(7); write_line("TURKOS/OS/K1315R-TRACE: RO-TO-RW-EACCES-NO-OWNER-CHANGE"); } else { park(); } 0 } 20 => { let (handle, next) = match stage() { 2 => (ORIGINAL, 3), 4 => (COPY, 5), 8 => (COPY, 9), _ => park(), }; if frame.x[0] != handle || handles().file_read(owner, Handle::from_raw(handle)) != Ok(41) { park(); } frame.x[0] = 41; set_stage(next); write_line("TURKOS/OS/K1315R-TRACE: FILE-READ-ALLOWED"); 0 } 26 => { let (handle, allowed, next) = match stage() { 3 => (ORIGINAL, true, 4), 5 => (COPY, false, 6), 7 => (ORIGINAL, true, 8), _ => park(), }; if frame.x[0] != handle { park(); } let before = handles().snapshot(); let result = handles().file_write(owner, Handle::from_raw(handle)); if handles().snapshot() != before { park(); } if allowed { if result != Ok(41) { park(); } frame.x[0] = 41; write_line("TURKOS/OS/K1315R-TRACE: ORIGINAL-WRITE-ALLOWED"); } else { if result != Err(Error::Access) { park(); } frame.x[0] = abi::AbiResult::Err(abi::Errno::Access).encode(); write_line("TURKOS/OS/K1315R-TRACE: COPY-WRITE-EACCES-NO-OWNER-CHANGE"); } set_stage(next); 0 } 22 => { let (handle, next, remaining) = match stage() { 10 => (ORIGINAL, 11, 1), 11 => (COPY, 12, 0), _ => park(), }; if frame.x[0] != handle || handles().close(owner, Handle::from_raw(handle)).is_err() || handles().len() != remaining { park(); } frame.x[0] = 0; set_stage(next); 0 } _ => { frame.x[0] = abi::AbiResult::Err(abi::Errno::NoSys).encode(); 0 } } } else if frame.x[7] == 2 && frame.x[8] == 3 { let request = turkos_os_user_runtime_abi::decode( frame.x[7], frame.x[8], core::array::from_fn(|i| frame.x[i])) .unwrap_or_else(|_| park()); let turkos_os_user_runtime_abi::Request::Exit(code) = request else { park(); }; if episode.case == 0 { if code != 0 || episode.calls != 2 || stage() != 13 { park(); } } else if code != 42 || episode.calls != 1 || stage() != 12 || handles().owned(episode.pid) != 0 { park(); } process_table.exit_current(code).unwrap_or_else(|_| park()); let outcome = Outcome::Exited(code); if process_table.record(episode.pid).map(|r| r.outcome) != Ok(Some(outcome)) { park(); } if episode.case == 1 { if handles().close_owner(episode.pid) != 0 || handles().len() != 0 { park(); } set_stage(13); write_line("TURKOS/OS/K1315R-TRACE: CHILD-EXIT-OWNER-EMPTY"); } episode.outcome = Some(outcome); 1 } else { if frame.x[7] != abi::ABI_VERSION { park(); } let mut abi_frame = turkos_os_exceptions::exceptions::Frame::EMPTY; abi_frame.x = frame.x; let call = abi::decode_call(&abi_frame).unwrap_or_else(|_| park()); if call == abi::Call::Role { if episode.calls != 0 { park(); } episode.calls = 1; frame.x[0] = episode.case as u64; return 0; } if let abi::Call::RunChild(1) = call { if episode.case != 0 || episode.calls != 1 || stage() != 0 { park(); } return os_k1215a_run_child(episode as *mut _, frame as *mut _, episode.launch.kernel_root); } if let abi::Call::Write(request) = call { if episode.case != 1 || request.fd() != 1 || request.bytes() != 16 || stage() != 9 { park(); } let mut bytes = [0u8; 16]; process_table.copy_current_user_data(episode.pid, request.address(), &mut bytes) .unwrap_or_else(|_| park()); if &bytes != b"RIGHTS:RW>RO:13\n" { park(); } for byte in bytes { write_byte(byte); } frame.x[0] = 16; set_stage(10); write_line("TURKOS/OS/K1315R-TRACE: USER-WROTE-RIGHTS-RESULT"); return 0; } park() } }