From 89d532a2673b11dc78f4f101804472fa62be85a4 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 8 Jul 2024 20:44:10 +0200 Subject: [PATCH] Fix half of proc scheme errors. --- src/common/int_like.rs | 9 + src/context/context.rs | 5 +- src/context/list.rs | 17 +- src/context/mod.rs | 15 +- src/context/process.rs | 43 +- src/main.rs | 25 +- src/scheme/proc.rs | 1861 +++++++++++++++++----------------- src/scheme/sys/scheme.rs | 3 +- src/scheme/sys/scheme_num.rs | 3 +- src/scheme/user.rs | 8 +- src/syscall/fs.rs | 13 +- src/syscall/mod.rs | 4 +- src/syscall/process.rs | 84 +- 13 files changed, 1111 insertions(+), 979 deletions(-) diff --git a/src/common/int_like.rs b/src/common/int_like.rs index a6b5a9f1e5..bad1534ae4 100644 --- a/src/common/int_like.rs +++ b/src/common/int_like.rs @@ -93,6 +93,15 @@ macro_rules! int_like { } #[allow(dead_code)] #[inline] + pub fn fetch_add( + &self, + with: $new_type_name, + order: ::core::sync::atomic::Ordering, + ) -> $new_type_name { + $new_type_name::from(self.container.fetch_add(with.into(), order)) + } + #[allow(dead_code)] + #[inline] pub fn compare_exchange( &self, current: $new_type_name, diff --git a/src/context/context.rs b/src/context/context.rs index 67eb728c5d..db9a394ae1 100644 --- a/src/context/context.rs +++ b/src/context/context.rs @@ -26,7 +26,8 @@ use ::core::sync::atomic::AtomicUsize; use super::{ empty_cr3, - memory::{AddrSpaceWrapper, GrantFileRef}, process::{Process, ProcessId}, + memory::{AddrSpaceWrapper, GrantFileRef}, + process::{Process, ProcessId}, }; int_like!(ContextId, AtomicContextId, usize, AtomicUsize); @@ -65,7 +66,7 @@ pub enum HardBlockedReason { AwaitingMmap { file_ref: GrantFileRef }, // TODO: PageFaultOom? NotYetStarted, - // TODO: ptrace_stop? + PtraceStop, } #[derive(Copy, Clone, Debug)] diff --git a/src/context/list.rs b/src/context/list.rs index 813fb53fbf..0d2ec864d0 100644 --- a/src/context/list.rs +++ b/src/context/list.rs @@ -5,10 +5,12 @@ use spinning_top::RwSpinlock; use super::{ context::{Context, ContextId, Kstack}, - memory::AddrSpaceWrapper, process::{Process, ProcessId}, + memory::AddrSpaceWrapper, + process::{new_process, Process, ProcessId, ProcessInfo}, }; use crate::{ interrupt::InterruptStack, + scheme::SchemeNamespace, syscall::error::{Error, Result, EAGAIN}, }; @@ -60,7 +62,10 @@ impl ContextList { assert!(self .map // TODO - .insert(cid, Arc::new(RwSpinlock::new(Context::new(cid, pid, process)?))) + .insert( + cid, + Arc::new(RwSpinlock::new(Context::new(cid, pid, process)?)) + ) .is_none()); Ok(self @@ -70,7 +75,10 @@ impl ContextList { } /// Create a new context. - pub fn new_context(&mut self, process: Arc>) -> Result<&Arc>> { + pub fn new_context( + &mut self, + process: Arc>, + ) -> Result<&Arc>> { let pid = process.read().pid; // Zero is not a valid context ID, therefore add 1. @@ -102,11 +110,12 @@ impl ContextList { pub fn spawn( &mut self, userspace_allowed: bool, + process: Arc>, func: extern "C" fn(), ) -> Result<&Arc>> { let stack = Kstack::new()?; - let context_lock = self.new_context()?; + let context_lock = self.new_context(process)?; { let mut context = context_lock.write(); let _ = context.set_addr_space(Some(AddrSpaceWrapper::new()?)); diff --git a/src/context/mod.rs b/src/context/mod.rs index a1896997b1..46dad92aeb 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -2,7 +2,7 @@ //! //! For resources on contexts, please consult [wikipedia](https://en.wikipedia.org/wiki/Context_switch) and [osdev](https://wiki.osdev.org/Context_Switching) -use alloc::{borrow::Cow, sync::Arc}; +use alloc::{borrow::Cow, sync::Arc, vec::Vec}; use spin::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use spinning_top::RwSpinlock; @@ -11,10 +11,11 @@ use crate::{ cpu_set::LogicalCpuSet, paging::{RmmA, RmmArch, TableKind}, percpu::PercpuBlock, + sync::WaitMap, syscall::error::{Error, Result, ESRCH}, }; -use self::process::ProcessId; +use self::process::{Process, ProcessId, ProcessInfo}; pub use self::{ context::{BorrowedHtBuf, Context, ContextId, Status, WaitpidKey}, list::ContextList, @@ -73,8 +74,16 @@ pub use self::arch::empty_cr3; pub fn init() { let mut contexts = contexts_mut(); let id = ContextId::from(crate::cpu_id().get() as usize + 1); + + let pid = ProcessId::new(0); + let process = Arc::new(RwLock::new(Process { + info: ProcessInfo::default(), + waitpid: Arc::new(WaitMap::new()), + threads: Vec::new(), + })); + let context_lock = contexts - .insert_context_raw(id) + .insert_context_raw(id, pid, process) .expect("could not initialize first context"); let mut context = context_lock.write(); context.sched_affinity = LogicalCpuSet::empty(); diff --git a/src/context/process.rs b/src/context/process.rs index ea9fe28940..47add91ab7 100644 --- a/src/context/process.rs +++ b/src/context/process.rs @@ -1,21 +1,28 @@ -use core::ops::{Deref, DerefMut}; +use core::{ + ops::{Deref, DerefMut}, + sync::atomic::{AtomicUsize, Ordering}, +}; // TODO: move all this code to userspace -use alloc::collections::BTreeMap; -use alloc::sync::{Arc, Weak}; -use alloc::vec::Vec; +use alloc::{ + collections::BTreeMap, + sync::{Arc, Weak}, + vec::Vec, +}; use spin::RwLock; use spinning_top::RwSpinlock; -use syscall::{Error, Result, ESRCH}; +use syscall::{Error, Result, ENOMEM, ESRCH}; -use crate::scheme::{CallerCtx, SchemeNamespace}; -use crate::sync::WaitMap; +use crate::{ + scheme::{CallerCtx, SchemeNamespace}, + sync::WaitMap, +}; use crate::context::{self, Context, WaitpidKey}; -int_like!(ProcessId, usize); +int_like!(ProcessId, AtomicProcessId, usize, AtomicUsize); #[derive(Debug)] pub struct Process { @@ -25,7 +32,7 @@ pub struct Process { /// Threads of process pub threads: Vec>>, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Default)] pub struct ProcessInfo { /// The process ID of this process pub pid: ProcessId, @@ -63,7 +70,10 @@ impl DerefMut for Process { } } -pub static PROCESSES: RwLock>>> = RwLock::new(BTreeMap::new()); +pub const INIT: ProcessId = ProcessId::new(1); +static NEXT_PID: AtomicProcessId = AtomicProcessId::new(INIT); +pub static PROCESSES: RwLock>>> = + RwLock::new(BTreeMap::new()); /// Get an iterator of all parents pub fn ancestors( @@ -82,7 +92,9 @@ pub fn ancestors( pub fn current() -> Result>> { let pid = context::current()?.read().pid; - Ok(Arc::clone(PROCESSES.read().get(&pid).ok_or(Error::new(ESRCH))?)) + Ok(Arc::clone( + PROCESSES.read().get(&pid).ok_or(Error::new(ESRCH))?, + )) } impl Process { pub fn caller_ctx(&self) -> CallerCtx { @@ -93,3 +105,12 @@ impl Process { } } } +pub fn new_process(info: impl FnOnce(ProcessId) -> ProcessInfo) -> Result>> { + let pid = NEXT_PID.fetch_add(ProcessId::new(1), Ordering::Relaxed); + Arc::try_new(RwLock::new(Process { + waitpid: Arc::try_new(WaitMap::new()).map_err(|_| Error::new(ENOMEM))?, + threads: Vec::new(), + info: info(pid), + })) + .map_err(|_| Error::new(ENOMEM)) +} diff --git a/src/main.rs b/src/main.rs index 80c4ff354b..03f9029212 100644 --- a/src/main.rs +++ b/src/main.rs @@ -61,7 +61,13 @@ extern crate bitflags; use core::sync::atomic::{AtomicU32, Ordering}; -use crate::{context::switch::SwitchResult, scheme::SchemeNamespace}; +use crate::{ + context::{ + process::{new_process, ProcessInfo, INIT}, + switch::SwitchResult, + }, + scheme::SchemeNamespace, +}; use crate::consts::*; @@ -197,7 +203,22 @@ fn kmain(cpu_count: u32, bootstrap: Bootstrap) -> ! { #[cfg(feature = "profiling")] profiling::ready_for_profiling(); - match context::contexts_mut().spawn(true, userspace_init) { + let process = new_process(|_| ProcessInfo { + pid: INIT, + ppid: INIT, + pgid: INIT, + session_id: INIT, + ruid: 0, + rgid: 0, + euid: 0, + egid: 0, + rns: SchemeNamespace::new(0), + ens: SchemeNamespace::new(0), + umask: 0o22, + }) + .expect("failed to create init process"); + + match context::contexts_mut().spawn(true, process, userspace_init) { Ok(context_lock) => { let mut context = context_lock.write(); context.status = context::Status::Runnable; diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index be129279f8..45f0b20eec 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -5,6 +5,7 @@ use crate::{ context::{HardBlockedReason, SignalState}, file::{FileDescriptor, InternalFlags}, memory::{handle_notify_files, AddrSpaceWrapper, Grant, PageSpan}, + process::{self, Process, ProcessId}, Context, ContextId, Status, }, memory::PAGE_SIZE, @@ -79,11 +80,16 @@ where return Err(Error::new(EBADF)); } // Stop process - let (was_stopped, mut running) = with_context_mut(pid, |context| { - let was_stopped = context.ptrace_stop; - context.ptrace_stop = true; - - Ok((was_stopped, context.running)) + let (prev_status, mut running) = with_context_mut(pid, |context| { + Ok(( + core::mem::replace( + &mut context.status, + context::Status::HardBlocked { + reason: HardBlockedReason::NotYetStarted, + }, + ), + context.running, + )) })?; // Wait until stopped @@ -101,7 +107,7 @@ where let ret = callback(context); - context.ptrace_stop = was_stopped; + context.status = prev_status; ret }) @@ -114,29 +120,40 @@ enum RegsKind { Env, } #[derive(Clone)] -enum Operation { - Regs(RegsKind), - Trace, - Static(&'static str), - Name, +enum ProcHandle { + Trace { + pid: ProcessId, + clones: Vec, + excl: bool, + }, + Static { + ty: &'static str, + bytes: Box<[u8]>, + }, SessionId, + Attr { + attr: Attr, + }, +} +#[derive(Clone)] +enum ContextHandle { + Regs(RegsKind), + Name, Sighandler, Start, - Attr(Attr), NewFiletable { filetable: Arc>>>, + data: Box<[u8]>, }, Filetable { filetable: Weak>>>, + data: Box<[u8]>, }, AddrSpace { addrspace: Arc, }, CurrentAddrSpace, - // "operations CAN change". The reason we split changing the address space into two handle - // types, is that we would rather want the actual switch to occur when closing, as opposed to - // when writing. This is so that we can actually guarantee that no file descriptors are leaked. AwaitingAddrSpaceChange { new: Arc, new_sp: usize, @@ -145,7 +162,9 @@ enum Operation { CurrentFiletable, - AwaitingFiletableChange(Arc>>>), + AwaitingFiletableChange { + new_ft: Arc>>>, + }, // TODO: Remove this once openat is implemented, or allow openat-via-dup via e.g. the top-level // directory. @@ -154,79 +173,52 @@ enum Operation { MmapMinAddr(Arc), } +#[derive(Clone)] +enum Handle { + Context { + context: Arc>, + kind: ContextHandle, + }, + Process { + process: Arc>, + kind: ProcHandle, + }, +} #[derive(Clone, Copy, PartialEq, Eq)] enum Attr { Uid, Gid, // TODO: namespace, tid, etc. } -impl Operation { +impl Handle { fn needs_child_process(&self) -> bool { matches!( self, - Self::Regs(_) - | Self::Trace - | Self::SessionId - | Self::Filetable { .. } - | Self::NewFiletable { .. } - | Self::AddrSpace { .. } - | Self::CurrentAddrSpace - | Self::CurrentFiletable - | Self::Sighandler + Self::Process { + kind: ProcHandle::Trace { .. } | ProcHandle::SessionId, + .. + } | Self::Context { + kind: ContextHandle::Regs(_) + | ContextHandle::Filetable { .. } + | ContextHandle::NewFiletable { .. } + | ContextHandle::AddrSpace { .. } + | ContextHandle::CurrentAddrSpace + | ContextHandle::CurrentFiletable + | ContextHandle::Sighandler, + .. + } ) } fn needs_root(&self) -> bool { - matches!(self, Self::Attr(_)) + matches!( + self, + Self::Process { + kind: Attr { .. }, + .. + } + ) } } -#[derive(Default)] -struct TraceData { - clones: Vec, -} -struct StaticData { - buf: Box<[u8]>, -} -impl StaticData { - fn new(buf: Box<[u8]>) -> Self { - Self { buf } - } -} -enum OperationData { - Trace(TraceData), - Static(StaticData), - Offset, - Other, -} -impl OperationData { - fn trace_data(&mut self) -> Option<&mut TraceData> { - match self { - OperationData::Trace(data) => Some(data), - _ => None, - } - } - fn static_data(&mut self) -> Option<&mut StaticData> { - match self { - OperationData::Static(data) => Some(data), - _ => None, - } - } -} - -#[derive(Clone)] -struct Info { - pid: ContextId, - cid: ContextId, - flags: usize, - - // Important: Operation must never change. Search for: - // - // "operations can't change" to see usages. - operation: Operation, -} -struct Handle { - info: Info, - data: OperationData, -} impl Handle { fn continue_ignored_children(&mut self) -> Option<()> { let data = self.data.trace_data()?; @@ -238,7 +230,9 @@ impl Handle { } if let Some(context) = contexts.get(pid) { let mut context = context.write(); - context.ptrace_stop = false; + context.status = context::Status::HardBlocked { + reason: HardBlockedReason::PtraceStop, + }; } } Some(()) @@ -265,19 +259,12 @@ fn get_context(id: ContextId) -> Result>> { } impl ProcScheme { - fn open_inner( - &self, - pid: ContextId, - operation_str: Option<&str>, - flags: usize, - uid: u32, - gid: u32, - ) -> Result<(usize, InternalFlags)> { - let (operation, positioned) = match operation_str { - Some("addrspace") => ( - Operation::AddrSpace { + fn openat_context(&self, path: &str, context: Arc>) { + let kind = match path { + "addrspace" => ( + ContextHandle::AddrSpace { addrspace: Arc::clone( - get_context(pid)? + context .read() .addr_space() .map_err(|_| Error::new(ENOENT))?, @@ -285,62 +272,82 @@ impl ProcScheme { }, true, ), - Some("filetable") => ( - Operation::Filetable { - filetable: Arc::downgrade(&get_context(pid)?.read().files), + "filetable" => ( + ContextHandle::Filetable { + filetable: Arc::downgrade(&context.read().files), + data: Box::new([]), }, true, ), - Some("current-addrspace") => (Operation::CurrentAddrSpace, false), - Some("current-filetable") => (Operation::CurrentFiletable, false), - Some("regs/float") => (Operation::Regs(RegsKind::Float), false), - Some("regs/int") => (Operation::Regs(RegsKind::Int), false), - Some("regs/env") => (Operation::Regs(RegsKind::Env), false), - Some("trace") => (Operation::Trace, false), - Some("exe") => (Operation::Static("exe"), true), - Some("name") => (Operation::Name, true), - Some("session_id") => (Operation::SessionId, true), - Some("sighandler") => (Operation::Sighandler, false), - Some("start") => (Operation::Start, false), - Some("uid") => (Operation::Attr(Attr::Uid), true), - Some("gid") => (Operation::Attr(Attr::Gid), true), - Some("open_via_dup") => (Operation::OpenViaDup, false), - Some("mmap-min-addr") => ( - Operation::MmapMinAddr(Arc::clone( - get_context(pid)? + "current-addrspace" => (ContextHandle::CurrentAddrSpace, false), + "current-filetable" => (ContextHandle::CurrentFiletable, false), + "regs/float" => (ContextHandle::Regs(RegsKind::Float), false), + "regs/int" => (ContextHandle::Regs(RegsKind::Int), false), + "regs/env" => (ContextHandle::Regs(RegsKind::Env), false), + "name" => (ContextHandle::Name, true), + "sighandler" => (ContextHandle::Sighandler, false), + "start" => (ContextHandle::Start, false), + "open_via_dup" => (ContextHandle::OpenViaDup, false), + "mmap-min-addr" => ( + ContextHandle::MmapMinAddr(Arc::clone( + context .read() .addr_space() .map_err(|_| Error::new(ENOENT))?, )), false, ), - Some("sched-affinity") => (Operation::SchedAffinity, true), + "sched-affinity" => (ContextHandle::SchedAffinity, true), _ => return Err(Error::new(EINVAL)), }; + } + fn open_inner( + &self, + pid: ProcessId, + operation_str: Option<&str>, + flags: usize, + uid: u32, + gid: u32, + ) -> Result<(usize, InternalFlags)> { + let processes = process::PROCESSES.read(); + let target = processes.get(&pid).ok_or(Error::new(ESRCH))?; - let contexts = context::contexts(); - let target = contexts.get(pid).ok_or(Error::new(ESRCH))?; + let (handle, positioned) = match operation_str.ok_or(Error::new(EINVAL))? { + "trace" => (ContextHandle::Trace { clones: Vec::new() }, false), + "exe" => ( + ContextHandle::Static { + ty: "exe", + bytes: target.read().name.as_bytes().to_owned(), + }, + true, + ), + "uid" => (ContextHandle::Attr { attr: Attr::Uid }, true), + "gid" => (ContextHandle::Attr { attr: Attr::Gid }, true), + "session_id" => (ContextHandle::SessionId, true), + + other => { + let first_thread = target + .read() + .threads + .first() + .ok_or(Error::new(ESRCH))? + .upgrade() + .ok_or(Error::new(ESRCH))?; + self.openat_context(other, first_thread) + } + }; let mut data; { let target = target.read(); - data = match operation { - Operation::Trace => OperationData::Trace(TraceData::default()), - Operation::Static(_) => OperationData::Static(StaticData::new( - target.name.clone().into_owned().into_bytes().into(), - )), - Operation::AddrSpace { .. } => OperationData::Offset, - _ => OperationData::Other, - }; - if let Status::Exited(_) = target.status { return Err(Error::new(ESRCH)); } // Unless root, check security - if operation.needs_child_process() && uid != 0 && gid != 0 { + if handle.needs_child_process() && uid != 0 && gid != 0 { let current = contexts.current().ok_or(Error::new(ESRCH))?; let current = current.read(); @@ -366,19 +373,23 @@ impl ProcScheme { None => return Err(Error::new(EPERM)), } } - } else if operation.needs_root() && (uid != 0 || gid != 0) { + } else if handle.needs_root() && (uid != 0 || gid != 0) { return Err(Error::new(EPERM)); } - let filetable_opt = match operation { - Operation::Filetable { ref filetable } => { - Some(filetable.upgrade().ok_or(Error::new(EOWNERDEAD))?) - } - Operation::NewFiletable { ref filetable } => Some(Arc::clone(filetable)), + let filetable_opt = match handle { + ContextHandle::Filetable { + ref filetable, + ref mut data, + } => Some((filetable.upgrade().ok_or(Error::new(EOWNERDEAD))?, data)), + ContextHandle::NewFiletable { + ref filetable, + ref mut data, + } => Some((Arc::clone(filetable), data)), _ => None, }; - if let Some(filetable) = filetable_opt { - data = OperationData::Static(StaticData::new({ + if let Some((filetable, data)) = filetable_opt { + *data = { use core::fmt::Write; let mut data = String::new(); @@ -391,20 +402,12 @@ impl ProcScheme { writeln!(data, "{}", index).unwrap(); } data.into_bytes().into_boxed_slice() - })); + }; } }; let (id, int_fl) = new_handle(( - Handle { - info: Info { - flags, - pid, - operation: operation.clone(), - cid: pid, - }, - data, - }, + handle, if positioned { InternalFlags::POSITIONED } else { @@ -412,7 +415,7 @@ impl ProcScheme { }, ))?; - if let Operation::Trace = operation { + if let ContextHandle::Trace = operation { if !ptrace::try_new_session(pid, id) { // There is no good way to handle id being occupied for nothing // here, is there? @@ -427,160 +430,6 @@ impl ProcScheme { Ok((id, int_fl)) } - - #[cfg(target_arch = "aarch64")] - fn read_env_regs(&self, info: &Info) -> Result { - use crate::device::cpu::registers::control_regs; - - let (tpidr_el0, tpidrro_el0) = if info.pid == context::context_id() { - unsafe { - ( - control_regs::tpidr_el0() as usize, - control_regs::tpidrro_el0() as usize, - ) - } - } else { - try_stop_context(info.pid, |context| { - Ok((context.arch.tpidr_el0, context.arch.tpidrro_el0)) - })? - }; - Ok(EnvRegisters { - tpidr_el0, - tpidrro_el0, - }) - } - - #[cfg(target_arch = "x86")] - fn read_env_regs(&self, info: &Info) -> Result { - let (fsbase, gsbase) = if info.pid == context::context_id() { - unsafe { - ( - (&*crate::gdt::pcr()).gdt[crate::gdt::GDT_USER_FS].offset() as u64, - (&*crate::gdt::pcr()).gdt[crate::gdt::GDT_USER_GS].offset() as u64, - ) - } - } else { - try_stop_context(info.pid, |context| { - Ok((context.arch.fsbase as u64, context.arch.gsbase as u64)) - })? - }; - Ok(EnvRegisters { - fsbase: fsbase as _, - gsbase: gsbase as _, - }) - } - - #[cfg(target_arch = "x86_64")] - fn read_env_regs(&self, info: &Info) -> Result { - // TODO: Avoid rdmsr if fsgsbase is not enabled, if this is worth optimizing for. - let (fsbase, gsbase) = if info.cid == context::current_cid() { - unsafe { - ( - x86::msr::rdmsr(x86::msr::IA32_FS_BASE), - x86::msr::rdmsr(x86::msr::IA32_KERNEL_GSBASE), - ) - } - } else { - try_stop_context(info.pid, |context| { - Ok((context.arch.fsbase as u64, context.arch.gsbase as u64)) - })? - }; - Ok(EnvRegisters { - fsbase: fsbase as _, - gsbase: gsbase as _, - }) - } - - #[cfg(target_arch = "aarch64")] - fn write_env_regs(&self, info: &Info, regs: EnvRegisters) -> Result<()> { - use crate::device::cpu::registers::control_regs; - - if info.pid == context::context_id() { - unsafe { - control_regs::tpidr_el0_write(regs.tpidr_el0 as u64); - control_regs::tpidrro_el0_write(regs.tpidrro_el0 as u64); - } - } else { - try_stop_context(info.pid, |context| { - context.arch.tpidr_el0 = regs.tpidr_el0; - context.arch.tpidrro_el0 = regs.tpidrro_el0; - Ok(()) - })?; - } - Ok(()) - } - - #[cfg(target_arch = "x86")] - fn write_env_regs(&self, info: &Info, regs: EnvRegisters) -> Result<()> { - if !(RmmA::virt_is_valid(VirtualAddress::new(regs.fsbase as usize)) - && RmmA::virt_is_valid(VirtualAddress::new(regs.gsbase as usize))) - { - return Err(Error::new(EINVAL)); - } - - if info.pid == context::context_id() { - unsafe { - (&mut *crate::gdt::pcr()).gdt[crate::gdt::GDT_USER_FS].set_offset(regs.fsbase); - (&mut *crate::gdt::pcr()).gdt[crate::gdt::GDT_USER_GS].set_offset(regs.gsbase); - - match context::contexts() - .current() - .ok_or(Error::new(ESRCH))? - .write() - .arch - { - ref mut arch => { - arch.fsbase = regs.fsbase as usize; - arch.gsbase = regs.gsbase as usize; - } - } - } - } else { - try_stop_context(info.pid, |context| { - context.arch.fsbase = regs.fsbase as usize; - context.arch.gsbase = regs.gsbase as usize; - Ok(()) - })?; - } - Ok(()) - } - - #[cfg(target_arch = "x86_64")] - fn write_env_regs(&self, info: &Info, regs: EnvRegisters) -> Result<()> { - if !(RmmA::virt_is_valid(VirtualAddress::new(regs.fsbase as usize)) - && RmmA::virt_is_valid(VirtualAddress::new(regs.gsbase as usize))) - { - return Err(Error::new(EINVAL)); - } - - if info.pid == context::current_cid() { - unsafe { - x86::msr::wrmsr(x86::msr::IA32_FS_BASE, regs.fsbase as u64); - // We have to write to KERNEL_GSBASE, because when the kernel returns to - // userspace, it will have executed SWAPGS first. - x86::msr::wrmsr(x86::msr::IA32_KERNEL_GSBASE, regs.gsbase as u64); - - match context::contexts() - .current() - .ok_or(Error::new(ESRCH))? - .write() - .arch - { - ref mut arch => { - arch.fsbase = regs.fsbase as usize; - arch.gsbase = regs.gsbase as usize; - } - } - } - } else { - try_stop_context(info.pid, |context| { - context.arch.fsbase = regs.fsbase as usize; - context.arch.gsbase = regs.gsbase as usize; - Ok(()) - })?; - } - Ok(()) - } } impl KernelScheme for ProcScheme { @@ -591,7 +440,7 @@ impl KernelScheme for ProcScheme { let pid = if pid_str == "current" { context::current_cid() } else if pid_str == "new" { - inherit_context()? + new_child()? } else if !FULL { return Err(Error::new(EACCES)); } else { @@ -602,26 +451,12 @@ impl KernelScheme for ProcScheme { .map(|(r, fl)| OpenResult::SchemeLocal(r, fl)) } - fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result { - let mut handles = HANDLES.write(); - let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; - - match cmd { - F_SETFL => { - handle.info.flags = arg; - Ok(0) - } - F_GETFL => Ok(handle.info.flags), - _ => Err(Error::new(EINVAL)), - } - } - fn fevent(&self, id: usize, _flags: EventFlags) -> Result { let handles = HANDLES.read(); let handle = handles.get(&id).ok_or(Error::new(EBADF))?; - match handle.info.operation { - Operation::Trace => ptrace::Session::with_session(handle.info.pid, |session| { + match handle { + ContextHandle::Trace { pid, .. } => ptrace::Session::with_session(pid, |session| { Ok(session.data.lock().session_fevent_flags()) }), _ => Ok(EventFlags::empty()), @@ -632,19 +467,21 @@ impl KernelScheme for ProcScheme { let mut handle = HANDLES.write().remove(&id).ok_or(Error::new(EBADF))?; handle.continue_ignored_children(); - let stop_context = if handle.info.cid == context::current_cid() { + let stop_context = if handle.cid == context::current_cid() { with_context_mut } else { try_stop_context }; - match handle.info.operation { - Operation::AwaitingAddrSpaceChange { + match handle { + ContextHandle::AwaitingAddrSpaceChange { new, new_sp, new_ip, } => { - let _ = stop_context(handle.info.pid, |context: &mut Context| { + let pid = context.read().pid; + + let _ = stop_context(pid, |context: &mut Context| { let regs = context.regs_mut().ok_or(Error::new(EBADFD))?; regs.set_instr_pointer(new_ip); regs.set_stack_pointer(new_sp); @@ -656,27 +493,24 @@ impl KernelScheme for ProcScheme { 0 )); } - Operation::AddrSpace { addrspace } | Operation::MmapMinAddr(addrspace) => { + ContextHandle::AddrSpace { addrspace } | ContextHandle::MmapMinAddr(addrspace) => { drop(addrspace) } - Operation::AwaitingFiletableChange(new) => { - with_context_mut(handle.info.pid, |context: &mut Context| { - context.files = new; - Ok(()) - })? + ContextHandle::AwaitingFiletableChange { new_ft } => { + context.write().files = new_ft; } - Operation::Trace => { - ptrace::close_session(handle.info.pid); + ContextHandle::Trace { pid, excl, .. } => { + ptrace::close_session(pid); - if handle.info.flags & O_EXCL == O_EXCL { - syscall::kill(handle.info.pid, SIGKILL, false)?; + if excl { + syscall::kill(pid, SIGKILL, false)?; } let contexts = context::contexts(); - if let Some(context) = contexts.get(handle.info.pid) { + if let Some(context) = contexts.get(pid) { let mut context = context.write(); - context.ptrace_stop = false; + context.status = context::Status::Runnable; } } _ => (), @@ -690,15 +524,13 @@ impl KernelScheme for ProcScheme { map: &crate::syscall::data::Map, consume: bool, ) -> Result { - let info = HANDLES - .read() - .get(&id) - .ok_or(Error::new(EBADF))? - .info - .clone(); + let handle = HANDLES.read().get(&id).ok_or(Error::new(EBADF))?.clone(); + let Handle::Context { context, kind } = handle else { + return Err(Error::new(EBADF)); + }; - match info.operation { - Operation::AddrSpace { ref addrspace } => { + match kind { + ContextHandle::AddrSpace { ref addrspace } => { if Arc::ptr_eq(addrspace, dst_addr_space) { return Err(Error::new(EBUSY)); } @@ -765,226 +597,16 @@ impl KernelScheme for ProcScheme { id: usize, buf: UserSliceWo, offset: u64, - _read_flags: u32, + read_flags: u32, _stored_flags: u32, ) -> Result { // Don't hold a global lock during the context switch later on - let info = { + let handle = { let handles = HANDLES.read(); - let handle = handles.get(&id).ok_or(Error::new(EBADF))?; - handle.info.clone() + handles.get(&id).ok_or(Error::new(EBADF))?.clone() }; - match info.operation { - Operation::Static(_) => { - let mut handles = HANDLES.write(); - let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; - read_from( - buf, - &handle.data.static_data().ok_or(Error::new(EBADFD))?.buf, - offset, - ) - } - Operation::Regs(kind) => { - union Output { - float: FloatRegisters, - int: IntRegisters, - env: EnvRegisters, - } - - let (output, size) = match kind { - RegsKind::Float => with_context(info.pid, |context| { - // NOTE: The kernel will never touch floats - - Ok(( - Output { - float: context.get_fx_regs(), - }, - mem::size_of::(), - )) - })?, - RegsKind::Int => try_stop_context(info.pid, |context| match context.regs() { - None => { - assert!(!context.running, "try_stop_context is broken, clearly"); - println!( - "{}:{}: Couldn't read registers from stopped process", - file!(), - line!() - ); - Err(Error::new(ENOTRECOVERABLE)) - } - Some(stack) => { - let mut regs = IntRegisters::default(); - stack.save(&mut regs); - Ok((Output { int: regs }, mem::size_of::())) - } - })?, - RegsKind::Env => ( - Output { - env: self.read_env_regs(&info)?, - }, - mem::size_of::(), - ), - }; - - let src_buf = - unsafe { slice::from_raw_parts(&output as *const _ as *const u8, size) }; - - buf.copy_common_bytes_from_slice(src_buf) - } - Operation::Trace => { - let mut handles = HANDLES.write(); - let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; - let data = handle.data.trace_data().expect("operations can't change"); - - // Wait for event - if handle.info.flags & O_NONBLOCK != O_NONBLOCK { - ptrace::wait(handle.info.pid)?; - } - - // Check if context exists - with_context(handle.info.pid, |_| Ok(()))?; - - let mut src_buf = [PtraceEvent::default(); 4]; - - // Read events - let src_len = src_buf.len(); - let slice = &mut src_buf - [..core::cmp::min(src_len, buf.len() / mem::size_of::())]; - - let (read, reached) = ptrace::Session::with_session(info.pid, |session| { - let mut data = session.data.lock(); - Ok((data.recv_events(slice), data.is_reached())) - })?; - - // Save child processes in a list of processes to restart - for event in &slice[..read] { - if event.cause == PTRACE_EVENT_CLONE { - data.clones.push(ContextId::from(event.a)); - } - } - - // If there are no events, and breakpoint isn't reached, we - // must not have waited. - if read == 0 && !reached { - assert!( - handle.info.flags & O_NONBLOCK == O_NONBLOCK, - "wait woke up spuriously??" - ); - return Err(Error::new(EAGAIN)); - } - - for (dst, src) in buf - .in_exact_chunks(mem::size_of::()) - .zip(slice.iter()) - { - dst.copy_exactly(src)?; - } - - // Return read events - Ok(read * mem::size_of::()) - } - Operation::AddrSpace { ref addrspace } => { - let OperationData::Offset = HANDLES.read().get(&id).ok_or(Error::new(EBADF))?.data - else { - return Err(Error::new(EBADFD)); - }; - let Ok(offset) = usize::try_from(offset) else { - return Ok(0); - }; - let grants_to_skip = offset / mem::size_of::(); - - // Output a list of grant descriptors, sufficient to allow relibc's fork() - // implementation to fmap MAP_SHARED grants. - let mut grants_read = 0; - - let mut dst = [GrantDesc::default(); 16]; - - for (dst, (grant_base, grant_info)) in dst - .iter_mut() - .zip(addrspace.acquire_read().grants.iter().skip(grants_to_skip)) - { - *dst = GrantDesc { - base: grant_base.start_address().data(), - size: grant_info.page_count() * PAGE_SIZE, - flags: grant_info.grant_flags(), - // The !0 is not a sentinel value; the availability of `offset` is - // indicated by the GRANT_SCHEME flag. - offset: grant_info.file_ref().map_or(!0, |f| f.base_offset as u64), - }; - grants_read += 1; - } - for (src, chunk) in dst - .iter() - .take(grants_read) - .zip(buf.in_exact_chunks(mem::size_of::())) - { - chunk.copy_exactly(src)?; - } - - Ok(grants_read * mem::size_of::()) - } - Operation::Name => read_from( - buf, - context::contexts() - .get(info.pid) - .ok_or(Error::new(ESRCH))? - .read() - .name - .as_bytes(), - offset, - ), - Operation::SessionId => read_from( - buf, - &context::contexts() - .get(info.pid) - .ok_or(Error::new(ESRCH))? - .read() - .session_id - .get() - .to_ne_bytes(), - offset, - ), - - Operation::Attr(attr) => { - let src_buf = match ( - attr, - &*Arc::clone(context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?) - .read(), - ) { - (Attr::Uid, context) => context.euid.to_string(), - (Attr::Gid, context) => context.egid.to_string(), - } - .into_bytes(); - - read_from(buf, &src_buf, offset) - } - Operation::Filetable { .. } => { - let mut handles = HANDLES.write(); - let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; - let data = handle.data.static_data().expect("operations can't change"); - - read_from(buf, &data.buf, offset) - } - Operation::MmapMinAddr(ref addrspace) => { - buf.write_usize(addrspace.acquire_read().mmap_min)?; - Ok(mem::size_of::()) - } - Operation::SchedAffinity => { - let mask = context::contexts() - .get(info.pid) - .ok_or(Error::new(EBADFD))? - .read() - .sched_affinity - .to_raw(); - - buf.copy_exactly(crate::cpu_set::mask_as_bytes(&mask))?; - Ok(mem::size_of_val(&mask)) - } - // TODO: Replace write() with SYS_DUP_FORWARD. - // TODO: Find a better way to switch address spaces, since they also require switching - // the instruction and stack pointer. Maybe remove `/regs` altogether and replace it - // with `/ctx` + match info { _ => Err(Error::new(EBADF)), } } @@ -999,16 +621,464 @@ impl KernelScheme for ProcScheme { // TODO: offset // Don't hold a global lock during the context switch later on - let info = { + let handle = { let mut handles = HANDLES.write(); let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; handle.continue_ignored_children(); - handle.info.clone() + handle.clone() }; - match info.operation { - Operation::Static(_) => Err(Error::new(EBADF)), - Operation::AddrSpace { addrspace } => { + match handle { + Handle::Process { process, kind } => kind.kwriteoff(process, buf), + Handle::Context { context, kind } => kind.kwriteoff(id, context, buf), + } + } + fn kfpath(&self, id: usize, buf: UserSliceWo) -> Result { + let handles = HANDLES.read(); + let handle = handles.get(&id).ok_or(Error::new(EBADF))?; + + let Handle::Process { process, kind } = handle else { + return Err(Error::new(EBADF)); + }; + let path = format!( + "proc:{}/{}", + pid, + match kind { + ProcHandle::Attr { + attr: Attr::Uid, .. + } => "uid", + ProcHandle::Attr { + attr: Attr::Gid, .. + } => "gid", + ProcHandle::Trace { .. } => "trace", + ProcHandle::Static { ty, .. } => ty, + ProcHandle::SessionId => "session_id", + }, + ); + /*let path = format!( + "proc:{}/{}", + handle.pid.get(), + match handle { + ContextHandle::Regs(RegsKind::Float) => "regs/float", + ContextHandle::Regs(RegsKind::Int) => "regs/int", + ContextHandle::Regs(RegsKind::Env) => "regs/env", + ContextHandle::Name => "name", + ContextHandle::Sighandler => "sighandler", + ContextHandle::Filetable { .. } => "filetable", + ContextHandle::AddrSpace { .. } => "addrspace", + ContextHandle::CurrentAddrSpace => "current-addrspace", + ContextHandle::CurrentFiletable => "current-filetable", + ContextHandle::OpenViaDup => "open-via-dup", + ContextHandle::MmapMinAddr(_) => "mmap-min-addr", + ContextHandle::SchedAffinity => "sched-affinity", + + _ => return Err(Error::new(EOPNOTSUPP)), + } + );*/ + + buf.copy_common_bytes_from_slice(path.as_bytes()) + } + fn kfstat(&self, id: usize, buffer: UserSliceWo) -> Result<()> { + let handles = HANDLES.read(); + let handle = handles.get(&id).ok_or(Error::new(EBADF))?; + + buffer.copy_exactly(&Stat { + st_mode: MODE_FILE | 0o666, + st_size: handle.fsize()?, + + ..Stat::default() + })?; + + Ok(()) + } + + fn fsize(&self, id: usize) -> Result { + let mut handles = HANDLES.write(); + let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + + handle.fsize() + } + + /// Dup is currently used to implement clone() and execve(). + fn kdup(&self, old_id: usize, raw_buf: UserSliceRo, _: CallerCtx) -> Result { + let info = { + let handles = HANDLES.read(); + let handle = handles.get(&old_id).ok_or(Error::new(EBADF))?; + + handle.clone() + }; + + let handle = |h, positioned| { + ( + h, + if positioned { + InternalFlags::POSITIONED + } else { + InternalFlags::empty() + }, + ) + }; + let mut array = [0_u8; 64]; + if raw_buf.len() > array.len() { + return Err(Error::new(EINVAL)); + } + raw_buf.copy_to_slice(&mut array[..raw_buf.len()])?; + let buf = &array[..raw_buf.len()]; + + new_handle(match info { + ContextHandle::OpenViaDup => { + let (uid, gid) = match &*process::current()?.read() { + process => (process.euid, process.egid), + }; + return self + .open_inner( + info.pid, + Some(core::str::from_utf8(buf).map_err(|_| Error::new(EINVAL))?) + .filter(|s| !s.is_empty()), + O_RDWR | O_CLOEXEC, + uid, + gid, + ) + .map(|(r, fl)| OpenResult::SchemeLocal(r, fl)); + } + + ContextHandle::Filetable { + ref filetable, + ref data, + } => { + // TODO: Maybe allow userspace to either copy or transfer recently dupped file + // descriptors between file tables. + if buf != b"copy" { + return Err(Error::new(EINVAL)); + } + let filetable = filetable.upgrade().ok_or(Error::new(EOWNERDEAD))?; + + let new_filetable = Arc::try_new(RwLock::new(filetable.read().clone())) + .map_err(|_| Error::new(ENOMEM))?; + + handle( + ContextHandle::NewFiletable { + filetable: new_filetable, + data: data.clone(), + }, + true, + ) + } + ContextHandle::AddrSpace { ref addrspace } => { + const GRANT_FD_PREFIX: &[u8] = b"grant-fd-"; + + let kind = match buf { + // TODO: Better way to obtain new empty address spaces, perhaps using SYS_OPEN. But + // in that case, what scheme? + b"empty" => ContextHandle::AddrSpace { + addrspace: AddrSpaceWrapper::new()?, + }, + b"exclusive" => ContextHandle::AddrSpace { + addrspace: addrspace.try_clone()?, + }, + b"mmap-min-addr" => ContextHandle::MmapMinAddr(Arc::clone(addrspace)), + + _ if buf.starts_with(GRANT_FD_PREFIX) => { + let string = core::str::from_utf8(&buf[GRANT_FD_PREFIX.len()..]) + .map_err(|_| Error::new(EINVAL))?; + let page_addr = + usize::from_str_radix(string, 16).map_err(|_| Error::new(EINVAL))?; + + if page_addr % PAGE_SIZE != 0 { + return Err(Error::new(EINVAL)); + } + + let page = Page::containing_address(VirtualAddress::new(page_addr)); + + match addrspace + .acquire_read() + .grants + .contains(page) + .ok_or(Error::new(EINVAL))? + { + (_, info) => { + return Ok(OpenResult::External( + info.file_ref() + .map(|r| Arc::clone(&r.description)) + .ok_or(Error::new(EBADF))?, + )) + } + } + } + + _ => return Err(Error::new(EINVAL)), + }; + + handle(Handle::Context { context, kind }, true) + } + _ => return Err(Error::new(EINVAL)), + }) + .map(|(r, fl)| OpenResult::SchemeLocal(r, fl)) + } +} +extern "C" fn clone_handler() { + // This function will return to the syscall return assembly, and subsequently transition to + // usermode. +} + +fn new_child() -> Result { + let new_id = { + let current_process = process::current()?; + let new_context_lock = + Arc::clone(context::contexts_mut().spawn(true, current_process, clone_handler)?); + + // (Signals are initially disabled.) + + let mut new_context = new_context_lock.write(); + + new_context.status = Status::HardBlocked { + reason: HardBlockedReason::NotYetStarted, + }; + + new_context.cid + }; + + if ptrace::send_event(crate::syscall::ptrace_event!( + PTRACE_EVENT_CLONE, + new_id.into() + )) + .is_some() + { + // Freeze the clone, allow ptrace to put breakpoints + // to it before it starts + let contexts = context::contexts(); + let context = contexts + .get(new_id) + .expect("Newly created context doesn't exist??"); + let mut context = context.write(); + context.status = context::Status::HardBlocked { + reason: HardBlockedReason::PtraceStop, + }; + } + + Ok(new_id) +} +fn extract_scheme_number(fd: usize) -> Result<(KernelSchemes, usize)> { + let (scheme_id, number) = match &*context::contexts() + .current() + .ok_or(Error::new(ESRCH))? + .read() + .get_file(FileHandle::from(fd)) + .ok_or(Error::new(EBADF))? + .description + .read() + { + desc => (desc.scheme, desc.number), + }; + let scheme = scheme::schemes() + .get(scheme_id) + .ok_or(Error::new(ENODEV))? + .clone(); + + Ok((scheme, number)) +} +fn verify_scheme(scheme: KernelSchemes) -> Result<()> { + if !matches!( + scheme, + KernelSchemes::Global(GlobalSchemes::ProcFull | GlobalSchemes::ProcRestricted) + ) { + return Err(Error::new(EBADF)); + } + Ok(()) +} +impl Handle { + fn fsize(&self) -> Result { + match self { + Self::Process { + kind: ProcHandle::Static { ref bytes, .. }, + .. + } => Ok(bytes.len() as u64), + Self::Context { + kind: + ContextHandle::Filetable { ref data, .. } + | ContextHandle::NewFiletable { ref data, .. }, + .. + } => Ok(data.len() as u64), + _ => Ok(0), + } + } +} +impl ProcHandle { + fn kwriteoff(self, process: Arc>, buf: UserSliceRo) -> Result { + match self { + Self::Static { .. } => Err(Error::new(EBADF)), + Self::Trace { .. } => { + let op = buf.read_u64()?; + let op = PtraceFlags::from_bits(op).ok_or(Error::new(EINVAL))?; + + // Set next breakpoint + ptrace::Session::with_session(info.pid, |session| { + session.data.lock().set_breakpoint( + Some(op).filter(|op| op.intersects(PTRACE_STOP_MASK | PTRACE_EVENT_MASK)), + ); + Ok(()) + })?; + + if op.contains(PTRACE_STOP_SINGLESTEP) { + try_stop_context(info.pid, |context| match context.regs_mut() { + None => { + println!( + "{}:{}: Couldn't read registers from stopped process", + file!(), + line!() + ); + Err(Error::new(ENOTRECOVERABLE)) + } + Some(stack) => { + stack.set_singlestep(true); + Ok(()) + } + })?; + } + + // disable the ptrace_stop flag, which is used in some cases + // FIXME + /* + with_context_mut(info.pid, |context| { + context.ptrace_stop = false; + Ok(()) + })?; + */ + + // and notify the tracee's WaitCondition, which is used in other cases + ptrace::Session::with_session(info.pid, |session| { + session.tracee.notify(); + Ok(()) + })?; + + Ok(mem::size_of::()) + } + Self::Attr { attr } => { + // TODO: What limit? + let mut str_buf = [0_u8; 32]; + let bytes_copied = buf.copy_common_bytes_to_slice(&mut str_buf)?; + + let id = core::str::from_utf8(&str_buf[..bytes_copied]) + .map_err(|_| Error::new(EINVAL))? + .parse::() + .map_err(|_| Error::new(EINVAL))?; + + match attr { + Attr::Uid => process.write().euid = id, + Attr::Gid => process.write().egid = id, + } + Ok(buf.len()) + } + Self::SessionId => { + let session_id = ProcessId::new(buf.read_usize()?); + + if session_id != process.read().pid { + // Session ID can only be set to this process's ID + return Err(Error::new(EPERM)); + } + + for (_pid, process_lock) in process::PROCESSES.read().iter() { + if session_id == process_lock.read().pgid { + // The session ID cannot match the PGID of any process + return Err(Error::new(EPERM)); + } + } + + { + let mut process = process.write(); + process.pgid = session_id; + process.session_id = session_id; + } + + Ok(buf.len()) + } + } + } + fn kreadoff( + self, + id: usize, + process: Arc>, + buf: UserSliceWo, + offset: u64, + read_flags: u32, + ) -> Result { + match self { + Self::Static { bytes, .. } => { + let mut handles = HANDLES.write(); + let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + read_from(buf, &bytes, offset) + } + Self::Trace { pid, clones, .. } => { + // Wait for event + if (read_flags as usize) & O_NONBLOCK != O_NONBLOCK { + ptrace::wait(pid)?; + } + + // Check if process exists FIXME + //with_context(pid, |_| Ok(()))?; + + let mut src_buf = [PtraceEvent::default(); 4]; + + // Read events + let src_len = src_buf.len(); + let slice = &mut src_buf + [..core::cmp::min(src_len, buf.len() / mem::size_of::())]; + + let (read, reached) = ptrace::Session::with_session(info.pid, |session| { + let mut data = session.data.lock(); + Ok((data.recv_events(slice), data.is_reached())) + })?; + let mut handles = HANDLES.write(); + let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let data = handle.data.trace_data().expect("operations can't change"); + + // Save child processes in a list of processes to restart + for event in &slice[..read] { + if event.cause == PTRACE_EVENT_CLONE { + data.clones.push(ContextId::from(event.a)); + } + } + + // If there are no events, and breakpoint isn't reached, we + // must not have waited. + if read == 0 && !reached { + return Err(Error::new(EAGAIN)); + } + + for (dst, src) in buf + .in_exact_chunks(mem::size_of::()) + .zip(slice.iter()) + { + dst.copy_exactly(src)?; + } + + // Return read events + Ok(read * mem::size_of::()) + } + Self::SessionId => { + read_from(buf, &process.read().session_id.get().to_ne_bytes(), offset) + } + Self::Attr { attr } => { + let src_buf = match (attr, process.read()) { + (Attr::Uid, process) => process.euid.to_string(), + (Attr::Gid, process) => process.egid.to_string(), + } + .into_bytes(); + + read_from(buf, &src_buf, offset) + } + } + } +} +impl ContextHandle { + fn kwriteoff( + self, + id: usize, + context: Arc>, + buf: UserSliceRo, + ) -> Result { + match self { + Self::AddrSpace { addrspace } => { let mut chunks = buf.usizes(); let mut words_read = 0; let mut next = || { @@ -1060,7 +1130,7 @@ impl KernelScheme for ProcScheme { } Ok(words_read * mem::size_of::()) } - Operation::Regs(kind) => match kind { + ContextHandle::Regs(kind) => match kind { RegsKind::Float => { let regs = unsafe { buf.read_exact::()? }; @@ -1095,54 +1165,11 @@ impl KernelScheme for ProcScheme { } RegsKind::Env => { let regs = unsafe { buf.read_exact::()? }; - self.write_env_regs(&info, regs)?; + write_env_regs(context.read().cid, regs)?; Ok(mem::size_of::()) } }, - Operation::Trace => { - let op = buf.read_u64()?; - let op = PtraceFlags::from_bits(op).ok_or(Error::new(EINVAL))?; - - // Set next breakpoint - ptrace::Session::with_session(info.pid, |session| { - session.data.lock().set_breakpoint( - Some(op).filter(|op| op.intersects(PTRACE_STOP_MASK | PTRACE_EVENT_MASK)), - ); - Ok(()) - })?; - - if op.contains(PTRACE_STOP_SINGLESTEP) { - try_stop_context(info.pid, |context| match context.regs_mut() { - None => { - println!( - "{}:{}: Couldn't read registers from stopped process", - file!(), - line!() - ); - Err(Error::new(ENOTRECOVERABLE)) - } - Some(stack) => { - stack.set_singlestep(true); - Ok(()) - } - })?; - } - - // disable the ptrace_stop flag, which is used in some cases - with_context_mut(info.pid, |context| { - context.ptrace_stop = false; - Ok(()) - })?; - - // and notify the tracee's WaitCondition, which is used in other cases - ptrace::Session::with_session(info.pid, |session| { - session.tracee.notify(); - Ok(()) - })?; - - Ok(mem::size_of::()) - } - Operation::Name => { + ContextHandle::Name => { // TODO: What limit? let mut name_buf = [0_u8; 256]; let bytes_copied = buf.copy_common_bytes_to_slice(&mut name_buf)?; @@ -1156,32 +1183,7 @@ impl KernelScheme for ProcScheme { .name = utf8.into(); Ok(buf.len()) } - Operation::SessionId => { - let session_id = ContextId::new(buf.read_usize()?); - - if session_id != info.pid { - // Session ID can only be set to this process's ID - return Err(Error::new(EPERM)); - } - - for (_id, context_lock) in context::contexts().iter() { - if session_id == context_lock.read().pgid { - // The session ID cannot match the PGID of any process - return Err(Error::new(EPERM)); - } - } - - let context_lock = - Arc::clone(context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?); - { - let mut context = context_lock.write(); - context.pgid = session_id; - context.session_id = session_id; - } - - Ok(buf.len()) - } - Operation::Sighandler => { + ContextHandle::Sighandler => { let data = unsafe { buf.read_exact::()? }; if data.user_handler >= crate::USER_END_OFFSET @@ -1244,7 +1246,7 @@ impl KernelScheme for ProcScheme { Ok(mem::size_of::()) } - Operation::Start => match context::contexts() + ContextHandle::Start => match context::contexts() .get(info.pid) .ok_or(Error::new(ESRCH))? .write() @@ -1258,27 +1260,11 @@ impl KernelScheme for ProcScheme { } _ => return Err(Error::new(EINVAL)), }, - Operation::Attr(attr) => { - // TODO: What limit? - let mut str_buf = [0_u8; 32]; - let bytes_copied = buf.copy_common_bytes_to_slice(&mut str_buf)?; - - let id = core::str::from_utf8(&str_buf[..bytes_copied]) - .map_err(|_| Error::new(EINVAL))? - .parse::() - .map_err(|_| Error::new(EINVAL))?; - let context_lock = - Arc::clone(context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?); - - match attr { - Attr::Uid => context_lock.write().euid = id, - Attr::Gid => context_lock.write().egid = id, - } - Ok(buf.len()) + ContextHandle::Filetable { .. } | ContextHandle::NewFiletable { .. } => { + Err(Error::new(EBADF)) } - Operation::Filetable { .. } | Operation::NewFiletable { .. } => Err(Error::new(EBADF)), - Operation::CurrentFiletable => { + ContextHandle::CurrentFiletable => { let filetable_fd = buf.read_usize()?; let (hopefully_this_scheme, number) = extract_scheme_number(filetable_fd)?; verify_scheme(hopefully_this_scheme)?; @@ -1287,14 +1273,27 @@ impl KernelScheme for ProcScheme { let Entry::Occupied(mut entry) = handles.entry(number) else { return Err(Error::new(EBADF)); }; - let filetable = match &mut entry.get_mut().info.operation { - Operation::Filetable { ref filetable } => { - filetable.upgrade().ok_or(Error::new(EOWNERDEAD))? - } - Operation::NewFiletable { ref filetable } => { + let filetable = match *entry.get_mut() { + Handle::Process { .. } => return Err(Error::new(EBADF)), + Handle::Context { + kind: ContextHandle::Filetable { ref filetable, .. }, + .. + } => filetable.upgrade().ok_or(Error::new(EOWNERDEAD))?, + Handle::Context { + kind: + ContextHandle::NewFiletable { + ref filetable, + ref data, + }, + .. + } => { let ft = Arc::clone(&filetable); - entry.get_mut().info.operation = Operation::Filetable { - filetable: Arc::downgrade(&filetable), + *entry.get_mut() = Handle::Context { + kind: ContextHandle::Filetable { + filetable: Arc::downgrade(&filetable), + data: data.clone(), + }, + context: Arc::clone(&context), }; ft } @@ -1302,15 +1301,14 @@ impl KernelScheme for ProcScheme { _ => return Err(Error::new(EBADF)), }; - handles - .get_mut(&id) - .ok_or(Error::new(EBADF))? - .info - .operation = Operation::AwaitingFiletableChange(filetable); + *handles.get_mut(&id).ok_or(Error::new(EBADF))? = Handle::Context { + kind: ContextHandle::AwaitingFiletableChange { new_ft: filetable }, + context, + }; Ok(mem::size_of::()) } - Operation::CurrentAddrSpace { .. } => { + ContextHandle::CurrentAddrSpace { .. } => { let mut iter = buf.usizes(); let addrspace_fd = iter.next().ok_or(Error::new(EINVAL))??; let sp = iter.next().ok_or(Error::new(EINVAL))??; @@ -1320,28 +1318,26 @@ impl KernelScheme for ProcScheme { verify_scheme(hopefully_this_scheme)?; let mut handles = HANDLES.write(); - let Operation::AddrSpace { ref addrspace } = handles - .get(&number) - .ok_or(Error::new(EBADF))? - .info - .operation + let Handle::Context { + kind: ContextHandle::AddrSpace { ref addrspace }, + .. + } = handles.get(&number).ok_or(Error::new(EBADF))? else { return Err(Error::new(EBADF)); }; - handles - .get_mut(&id) - .ok_or(Error::new(EBADF))? - .info - .operation = Operation::AwaitingAddrSpaceChange { - new: Arc::clone(addrspace), - new_sp: sp, - new_ip: ip, + *handles.get_mut(&id).ok_or(Error::new(EBADF))? = Handle::Context { + context, + kind: Self::AwaitingAddrSpaceChange { + new: Arc::clone(addrspace), + new_sp: sp, + new_ip: ip, + }, }; Ok(3 * mem::size_of::()) } - Operation::MmapMinAddr(ref addrspace) => { + Self::MmapMinAddr(ref addrspace) => { let val = buf.read_usize()?; if val % PAGE_SIZE != 0 || val > crate::USER_END_OFFSET { return Err(Error::new(EINVAL)); @@ -1349,7 +1345,7 @@ impl KernelScheme for ProcScheme { addrspace.acquire_write().mmap_min = val; Ok(mem::size_of::()) } - Operation::SchedAffinity => { + Self::SchedAffinity => { let mask = unsafe { buf.read_exact::()? }; context::contexts() @@ -1361,277 +1357,284 @@ impl KernelScheme for ProcScheme { Ok(mem::size_of_val(&mask)) } - - _ => Err(Error::new(EBADF)), + Self::OpenViaDup + | Self::AwaitingAddrSpaceChange { .. } + | Self::AwaitingFiletableChange { .. } => Err(Error::new(EBADF)), } } - fn kfpath(&self, id: usize, buf: UserSliceWo) -> Result { - let handles = HANDLES.read(); - let handle = handles.get(&id).ok_or(Error::new(EBADF))?; + fn kreadoff( + &self, + id: usize, + context: Arc>, + buf: UserSliceWo, + offset: u64, + ) -> Result { + match self { + ContextHandle::Regs(kind) => { + union Output { + float: FloatRegisters, + int: IntRegisters, + env: EnvRegisters, + } - let path = format!( - "proc:{}/{}", - handle.info.pid.get(), - match handle.info.operation { - Operation::Regs(RegsKind::Float) => "regs/float", - Operation::Regs(RegsKind::Int) => "regs/int", - Operation::Regs(RegsKind::Env) => "regs/env", - Operation::Trace => "trace", - Operation::Static(path) => path, - Operation::Name => "name", - Operation::Sighandler => "sighandler", - Operation::Attr(Attr::Uid) => "uid", - Operation::Attr(Attr::Gid) => "gid", - Operation::Filetable { .. } => "filetable", - Operation::AddrSpace { .. } => "addrspace", - Operation::CurrentAddrSpace => "current-addrspace", - Operation::CurrentFiletable => "current-filetable", - Operation::OpenViaDup => "open-via-dup", - Operation::MmapMinAddr(_) => "mmap-min-addr", - Operation::SchedAffinity => "sched-affinity", + let (output, size) = match kind { + RegsKind::Float => with_context(info.pid, |context| { + // NOTE: The kernel will never touch floats - _ => return Err(Error::new(EOPNOTSUPP)), + Ok(( + Output { + float: context.get_fx_regs(), + }, + mem::size_of::(), + )) + })?, + RegsKind::Int => try_stop_context(info.pid, |context| match context.regs() { + None => { + assert!(!context.running, "try_stop_context is broken, clearly"); + println!( + "{}:{}: Couldn't read registers from stopped process", + file!(), + line!() + ); + Err(Error::new(ENOTRECOVERABLE)) + } + Some(stack) => { + let mut regs = IntRegisters::default(); + stack.save(&mut regs); + Ok((Output { int: regs }, mem::size_of::())) + } + })?, + RegsKind::Env => ( + Output { + env: read_env_regs(&info)?, + }, + mem::size_of::(), + ), + }; + + let src_buf = + unsafe { slice::from_raw_parts(&output as *const _ as *const u8, size) }; + + buf.copy_common_bytes_from_slice(src_buf) } - ); + ContextHandle::AddrSpace { ref addrspace } => { + let Ok(offset) = usize::try_from(offset) else { + return Ok(0); + }; + let grants_to_skip = offset / mem::size_of::(); - buf.copy_common_bytes_from_slice(path.as_bytes()) - } - fn kfstat(&self, id: usize, buffer: UserSliceWo) -> Result<()> { - let handles = HANDLES.read(); - let handle = handles.get(&id).ok_or(Error::new(EBADF))?; + // Output a list of grant descriptors, sufficient to allow relibc's fork() + // implementation to fmap MAP_SHARED grants. + let mut grants_read = 0; - buffer.copy_exactly(&Stat { - st_mode: MODE_FILE | 0o666, - st_size: match handle.data { - OperationData::Static(ref data) => data.buf.len() as u64, - _ => 0, - }, + let mut dst = [GrantDesc::default(); 16]; - ..Stat::default() - })?; + for (dst, (grant_base, grant_info)) in dst + .iter_mut() + .zip(addrspace.acquire_read().grants.iter().skip(grants_to_skip)) + { + *dst = GrantDesc { + base: grant_base.start_address().data(), + size: grant_info.page_count() * PAGE_SIZE, + flags: grant_info.grant_flags(), + // The !0 is not a sentinel value; the availability of `offset` is + // indicated by the GRANT_SCHEME flag. + offset: grant_info.file_ref().map_or(!0, |f| f.base_offset as u64), + }; + grants_read += 1; + } + for (src, chunk) in dst + .iter() + .take(grants_read) + .zip(buf.in_exact_chunks(mem::size_of::())) + { + chunk.copy_exactly(src)?; + } - Ok(()) - } - - fn fsize(&self, id: usize) -> Result { - let mut handles = HANDLES.write(); - let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; - - // TODO: operation-dependent - Ok(handle - .data - .static_data() - .ok_or(Error::new(ESPIPE))? - .buf - .len() as u64) - } - - /// Dup is currently used to implement clone() and execve(). - fn kdup(&self, old_id: usize, raw_buf: UserSliceRo, _: CallerCtx) -> Result { - let info = { - let handles = HANDLES.read(); - let handle = handles.get(&old_id).ok_or(Error::new(EBADF))?; - - handle.info.clone() - }; - - let handle = |operation, data, positioned| { - ( - Handle { - info: Info { - flags: 0, - pid: info.pid, - cid: info.cid, - operation, - }, - data, - }, - if positioned { - InternalFlags::POSITIONED - } else { - InternalFlags::empty() - }, - ) - }; - let mut array = [0_u8; 64]; - if raw_buf.len() > array.len() { - return Err(Error::new(EINVAL)); - } - raw_buf.copy_to_slice(&mut array[..raw_buf.len()])?; - let buf = &array[..raw_buf.len()]; - - new_handle(match info.operation { - Operation::OpenViaDup => { - let (uid, gid) = match &*context::contexts() - .current() + Ok(grants_read * mem::size_of::()) + } + ContextHandle::Name => read_from( + buf, + context::contexts() + .get(info.pid) .ok_or(Error::new(ESRCH))? .read() - { - context => (context.euid, context.egid), - }; - return self - .open_inner( - info.pid, - Some(core::str::from_utf8(buf).map_err(|_| Error::new(EINVAL))?) - .filter(|s| !s.is_empty()), - O_RDWR | O_CLOEXEC, - uid, - gid, - ) - .map(|(r, fl)| OpenResult::SchemeLocal(r, fl)); + .name + .as_bytes(), + offset, + ), + + ContextHandle::Filetable { data, .. } => read_from(buf, &data, offset), + ContextHandle::MmapMinAddr(ref addrspace) => { + buf.write_usize(addrspace.acquire_read().mmap_min)?; + Ok(mem::size_of::()) } + ContextHandle::SchedAffinity => { + let mask = context::contexts() + .get(info.pid) + .ok_or(Error::new(EBADFD))? + .read() + .sched_affinity + .to_raw(); - Operation::Filetable { ref filetable } => { - // TODO: Maybe allow userspace to either copy or transfer recently dupped file - // descriptors between file tables. - if buf != b"copy" { - return Err(Error::new(EINVAL)); - } - let filetable = filetable.upgrade().ok_or(Error::new(EOWNERDEAD))?; - - let new_filetable = Arc::try_new(RwLock::new(filetable.read().clone())) - .map_err(|_| Error::new(ENOMEM))?; - - handle( - Operation::NewFiletable { - filetable: new_filetable, - }, - OperationData::Other, - true, - ) - } - Operation::AddrSpace { ref addrspace } => { - const GRANT_FD_PREFIX: &[u8] = b"grant-fd-"; - - let operation = match buf { - // TODO: Better way to obtain new empty address spaces, perhaps using SYS_OPEN. But - // in that case, what scheme? - b"empty" => Operation::AddrSpace { - addrspace: AddrSpaceWrapper::new()?, - }, - b"exclusive" => Operation::AddrSpace { - addrspace: addrspace.try_clone()?, - }, - b"mmap-min-addr" => Operation::MmapMinAddr(Arc::clone(addrspace)), - - _ if buf.starts_with(GRANT_FD_PREFIX) => { - let string = core::str::from_utf8(&buf[GRANT_FD_PREFIX.len()..]) - .map_err(|_| Error::new(EINVAL))?; - let page_addr = - usize::from_str_radix(string, 16).map_err(|_| Error::new(EINVAL))?; - - if page_addr % PAGE_SIZE != 0 { - return Err(Error::new(EINVAL)); - } - - let page = Page::containing_address(VirtualAddress::new(page_addr)); - - match addrspace - .acquire_read() - .grants - .contains(page) - .ok_or(Error::new(EINVAL))? - { - (_, info) => { - return Ok(OpenResult::External( - info.file_ref() - .map(|r| Arc::clone(&r.description)) - .ok_or(Error::new(EBADF))?, - )) - } - } - } - - _ => return Err(Error::new(EINVAL)), - }; - - handle(operation, OperationData::Offset, true) - } - _ => return Err(Error::new(EINVAL)), - }) - .map(|(r, fl)| OpenResult::SchemeLocal(r, fl)) + buf.copy_exactly(crate::cpu_set::mask_as_bytes(&mask))?; + Ok(mem::size_of_val(&mask)) + } // TODO: Replace write() with SYS_DUP_FORWARD. + // TODO: Find a better way to switch address spaces, since they also require switching + // the instruction and stack pointer. Maybe remove `/regs` altogether and replace it + // with `/ctx` + } } } -extern "C" fn clone_handler() { - // This function will return to the syscall return assembly, and subsequently transition to - // usermode. -} +#[cfg(target_arch = "aarch64")] +fn write_env_regs(&self, info: &Info, regs: EnvRegisters) -> Result<()> { + use crate::device::cpu::registers::control_regs; -fn inherit_context() -> Result { - let new_id = { - let current_context_lock = - Arc::clone(context::contexts().current().ok_or(Error::new(ESRCH))?); - let new_context_lock = Arc::clone(context::contexts_mut().spawn(true, clone_handler)?); - - // (Signals are initially disabled.) - - let current_context = current_context_lock.read(); - let mut new_context = new_context_lock.write(); - - new_context.status = Status::HardBlocked { - reason: HardBlockedReason::NotYetStarted, - }; - - // TODO: Move all of these IDs into somewhere in userspace, file descriptors as - // capabilities. A userspace daemon can manage process hierarchies etc. whereas the kernel - // only needs to manage contexts. - new_context.euid = current_context.euid; - new_context.egid = current_context.egid; - new_context.ruid = current_context.ruid; - new_context.rgid = current_context.rgid; - new_context.ens = current_context.ens; - new_context.rns = current_context.rns; - new_context.ppid = current_context.pid; - new_context.pgid = current_context.pgid; - new_context.session_id = current_context.session_id; - new_context.umask = current_context.umask; - - new_context.cid - }; - - if ptrace::send_event(crate::syscall::ptrace_event!( - PTRACE_EVENT_CLONE, - new_id.into() - )) - .is_some() - { - // Freeze the clone, allow ptrace to put breakpoints - // to it before it starts - let contexts = context::contexts(); - let context = contexts - .get(new_id) - .expect("Newly created context doesn't exist??"); - let mut context = context.write(); - context.ptrace_stop = true; - } - - Ok(new_id) -} -fn extract_scheme_number(fd: usize) -> Result<(KernelSchemes, usize)> { - let (scheme_id, number) = match &*context::contexts() - .current() - .ok_or(Error::new(ESRCH))? - .read() - .get_file(FileHandle::from(fd)) - .ok_or(Error::new(EBADF))? - .description - .read() - { - desc => (desc.scheme, desc.number), - }; - let scheme = scheme::schemes() - .get(scheme_id) - .ok_or(Error::new(ENODEV))? - .clone(); - - Ok((scheme, number)) -} -fn verify_scheme(scheme: KernelSchemes) -> Result<()> { - if !matches!( - scheme, - KernelSchemes::Global(GlobalSchemes::ProcFull | GlobalSchemes::ProcRestricted) - ) { - return Err(Error::new(EBADF)); + if info.pid == context::context_id() { + unsafe { + control_regs::tpidr_el0_write(regs.tpidr_el0 as u64); + control_regs::tpidrro_el0_write(regs.tpidrro_el0 as u64); + } + } else { + try_stop_context(info.pid, |context| { + context.arch.tpidr_el0 = regs.tpidr_el0; + context.arch.tpidrro_el0 = regs.tpidrro_el0; + Ok(()) + })?; } Ok(()) } + +#[cfg(target_arch = "x86")] +fn write_env_regs(&self, info: &Info, regs: EnvRegisters) -> Result<()> { + if !(RmmA::virt_is_valid(VirtualAddress::new(regs.fsbase as usize)) + && RmmA::virt_is_valid(VirtualAddress::new(regs.gsbase as usize))) + { + return Err(Error::new(EINVAL)); + } + + if info.pid == context::context_id() { + unsafe { + (&mut *crate::gdt::pcr()).gdt[crate::gdt::GDT_USER_FS].set_offset(regs.fsbase); + (&mut *crate::gdt::pcr()).gdt[crate::gdt::GDT_USER_GS].set_offset(regs.gsbase); + + match context::contexts() + .current() + .ok_or(Error::new(ESRCH))? + .write() + .arch + { + ref mut arch => { + arch.fsbase = regs.fsbase as usize; + arch.gsbase = regs.gsbase as usize; + } + } + } + } else { + try_stop_context(info.pid, |context| { + context.arch.fsbase = regs.fsbase as usize; + context.arch.gsbase = regs.gsbase as usize; + Ok(()) + })?; + } + Ok(()) +} + +#[cfg(target_arch = "x86_64")] +fn write_env_regs(cid: ContextId, regs: EnvRegisters) -> Result<()> { + if !(RmmA::virt_is_valid(VirtualAddress::new(regs.fsbase as usize)) + && RmmA::virt_is_valid(VirtualAddress::new(regs.gsbase as usize))) + { + return Err(Error::new(EINVAL)); + } + + if cid == context::current_cid() { + unsafe { + x86::msr::wrmsr(x86::msr::IA32_FS_BASE, regs.fsbase as u64); + // We have to write to KERNEL_GSBASE, because when the kernel returns to + // userspace, it will have executed SWAPGS first. + x86::msr::wrmsr(x86::msr::IA32_KERNEL_GSBASE, regs.gsbase as u64); + + match context::contexts() + .current() + .ok_or(Error::new(ESRCH))? + .write() + .arch + { + ref mut arch => { + arch.fsbase = regs.fsbase as usize; + arch.gsbase = regs.gsbase as usize; + } + } + } + } else { + try_stop_context(cid, |context| { + context.arch.fsbase = regs.fsbase as usize; + context.arch.gsbase = regs.gsbase as usize; + Ok(()) + })?; + } + Ok(()) +} +#[cfg(target_arch = "aarch64")] +fn read_env_regs(&self, info: &Info) -> Result { + use crate::device::cpu::registers::control_regs; + + let (tpidr_el0, tpidrro_el0) = if info.pid == context::context_id() { + unsafe { + ( + control_regs::tpidr_el0() as usize, + control_regs::tpidrro_el0() as usize, + ) + } + } else { + try_stop_context(info.pid, |context| { + Ok((context.arch.tpidr_el0, context.arch.tpidrro_el0)) + })? + }; + Ok(EnvRegisters { + tpidr_el0, + tpidrro_el0, + }) +} + +#[cfg(target_arch = "x86")] +fn read_env_regs(&self, info: &Info) -> Result { + let (fsbase, gsbase) = if info.pid == context::context_id() { + unsafe { + ( + (&*crate::gdt::pcr()).gdt[crate::gdt::GDT_USER_FS].offset() as u64, + (&*crate::gdt::pcr()).gdt[crate::gdt::GDT_USER_GS].offset() as u64, + ) + } + } else { + try_stop_context(info.pid, |context| { + Ok((context.arch.fsbase as u64, context.arch.gsbase as u64)) + })? + }; + Ok(EnvRegisters { + fsbase: fsbase as _, + gsbase: gsbase as _, + }) +} + +#[cfg(target_arch = "x86_64")] +fn read_env_regs(cid: ContextId) -> Result { + // TODO: Avoid rdmsr if fsgsbase is not enabled, if this is worth optimizing for. + let (fsbase, gsbase) = if cid == context::current_cid() { + unsafe { + ( + x86::msr::rdmsr(x86::msr::IA32_FS_BASE), + x86::msr::rdmsr(x86::msr::IA32_KERNEL_GSBASE), + ) + } + } else { + try_stop_context(cid, |context| { + Ok((context.arch.fsbase as u64, context.arch.gsbase as u64)) + })? + }; + Ok(EnvRegisters { + fsbase: fsbase as _, + gsbase: gsbase as _, + }) +} diff --git a/src/scheme/sys/scheme.rs b/src/scheme/sys/scheme.rs index 8b137cbaf9..461cbb14a3 100644 --- a/src/scheme/sys/scheme.rs +++ b/src/scheme/sys/scheme.rs @@ -1,7 +1,8 @@ use alloc::vec::Vec; use crate::{ - context::process, scheme, + context::process, + scheme, syscall::error::{Error, Result}, }; diff --git a/src/scheme/sys/scheme_num.rs b/src/scheme/sys/scheme_num.rs index 1bd70cb713..eefce66cd0 100644 --- a/src/scheme/sys/scheme_num.rs +++ b/src/scheme/sys/scheme_num.rs @@ -1,7 +1,8 @@ use alloc::vec::Vec; use crate::{ - context::process, scheme, + context::process, + scheme, syscall::error::{Error, Result, ESRCH}, }; diff --git a/src/scheme/user.rs b/src/scheme/user.rs index 548ce2a04a..64919b3826 100644 --- a/src/scheme/user.rs +++ b/src/scheme/user.rs @@ -21,10 +21,14 @@ use syscall::{ use crate::{ context::{ - self, context::HardBlockedReason, file::{FileDescription, FileDescriptor, InternalFlags}, memory::{ + self, + context::HardBlockedReason, + file::{FileDescription, FileDescriptor, InternalFlags}, + memory::{ AddrSpace, AddrSpaceWrapper, BorrowedFmapSource, Grant, GrantFileRef, MmapMode, PageSpan, DANGLING, - }, process, BorrowedHtBuf, Context, Status + }, + process, BorrowedHtBuf, Context, Status, }, event, memory::Frame, diff --git a/src/syscall/fs.rs b/src/syscall/fs.rs index f4dd9f3310..37c02957b8 100644 --- a/src/syscall/fs.rs +++ b/src/syscall/fs.rs @@ -7,7 +7,8 @@ use crate::{ context::{ self, file::{FileDescription, FileDescriptor, InternalFlags}, - memory::{AddrSpace, PageSpan}, process, + memory::{AddrSpace, PageSpan}, + process, }, paging::{Page, VirtualAddress, PAGE_SIZE}, scheme::{self, CallerCtx, FileHandle, KernelScheme, OpenResult}, @@ -169,7 +170,10 @@ pub fn close(fd: FileHandle) -> Result<()> { fn duplicate_file(fd: FileHandle, user_buf: UserSliceRo) -> Result { let caller_ctx = process::current()?.read().caller_ctx(); - let file = context::current()?.read().get_file(fd).ok_or(Error::new(EBADF))?; + let file = context::current()? + .read() + .get_file(fd) + .ok_or(Error::new(EBADF))?; if user_buf.is_empty() { Ok(FileDescriptor { @@ -360,7 +364,10 @@ pub fn frename(fd: FileHandle, raw_path: UserSliceRo) -> Result<()> { process.ens, ), }; - let file = context::current()?.read().get_file(fd).ok_or(Error::new(EBADF))?; + let file = context::current()? + .read() + .get_file(fd) + .ok_or(Error::new(EBADF))?; /* let mut path_buf = BorrowedHtBuf::head()?; diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs index 3bac33565a..3c1d1ad223 100644 --- a/src/syscall/mod.rs +++ b/src/syscall/mod.rs @@ -236,7 +236,9 @@ pub fn syscall( )?), SYS_SETPGID => setpgid(ProcessId::from(b), ProcessId::from(c)).map(|()| 0), SYS_SETREUID => setreuid(b as u32, c as u32).map(|()| 0), - SYS_SETRENS => setrens(SchemeNamespace::from(b), SchemeNamespace::from(c)).map(|()| 0), + SYS_SETRENS => { + setrens(SchemeNamespace::from(b), SchemeNamespace::from(c)).map(|()| 0) + } SYS_SETREGID => setregid(b as u32, c as u32).map(|()| 0), SYS_UMASK => umask(b), SYS_VIRTTOPHYS => virttophys(b), diff --git a/src/syscall/process.rs b/src/syscall/process.rs index f5e6174968..d7f7cc26a6 100644 --- a/src/syscall/process.rs +++ b/src/syscall/process.rs @@ -6,7 +6,9 @@ use rmm::Arch; use spin::RwLock; use crate::context::{ - memory::{AddrSpace, Grant, PageSpan}, process::{self, ProcessId, ProcessInfo}, WaitpidKey + memory::{AddrSpace, Grant, PageSpan}, + process::{self, ProcessId, ProcessInfo}, + WaitpidKey, }; use crate::{ @@ -116,7 +118,12 @@ pub fn getpgid(pid: ProcessId) -> Result { let process_lock = if pid.get() == 0 { process::current()? } else { - Arc::clone(process::PROCESSES.read().get(&pid).ok_or(Error::new(ESRCH))?) + Arc::clone( + process::PROCESSES + .read() + .get(&pid) + .ok_or(Error::new(ESRCH))?, + ) }; let process = process_lock.read(); Ok(process.pgid) @@ -254,7 +261,8 @@ pub fn kill(pid: ProcessId, sig: usize, parent_sigchld: bool) -> Result { } => { sent += 1; let waitpid = Arc::clone( - &process::PROCESSES.read() + &process::PROCESSES + .read() .get(&ppid) .ok_or(Error::new(ESRCH))? .read() @@ -271,17 +279,19 @@ pub fn kill(pid: ProcessId, sig: usize, parent_sigchld: bool) -> Result { } SendResult::SucceededSigcont { ppid, pgid } => { sent += 1; - &process::PROCESSES.read() - .get(&ppid) - .ok_or(Error::new(ESRCH))? - .read() - .waitpid.send( - WaitpidKey { - pid: Some(pid), - pgid: Some(pgid), - }, - (pid, 0xffff), - ); + &process::PROCESSES + .read() + .get(&ppid) + .ok_or(Error::new(ESRCH))? + .read() + .waitpid + .send( + WaitpidKey { + pid: Some(pid), + pgid: Some(pgid), + }, + (pid, 0xffff), + ); } } Ok(()) @@ -293,7 +303,15 @@ pub fn kill(pid: ProcessId, sig: usize, parent_sigchld: bool) -> Result { found += 1; let (context_lock, info) = { let process = process_lock.read(); - (process.threads.first().ok_or(Error::new(ESRCH))?.upgrade().ok_or(Error::new(ESRCH))?, process.info) + ( + process + .threads + .first() + .ok_or(Error::new(ESRCH))? + .upgrade() + .ok_or(Error::new(ESRCH))?, + process.info, + ) }; let mut context = context_lock.write(); let result = send(&mut *context, &info); @@ -304,7 +322,15 @@ pub fn kill(pid: ProcessId, sig: usize, parent_sigchld: bool) -> Result { for (pid, process_lock) in processes.iter() { let (context_lock, info) = { let process = process_lock.read(); - (process.threads.first().ok_or(Error::new(ESRCH))?.upgrade().ok_or(Error::new(ESRCH))?, process.info) + ( + process + .threads + .first() + .ok_or(Error::new(ESRCH))? + .upgrade() + .ok_or(Error::new(ESRCH))?, + process.info, + ) }; if info.pid.get() <= 2 { @@ -328,7 +354,15 @@ pub fn kill(pid: ProcessId, sig: usize, parent_sigchld: bool) -> Result { for (pid, process_lock) in processes.iter() { let (context_lock, info) = { let process = process_lock.read(); - (process.threads.first().ok_or(Error::new(ESRCH))?.upgrade().ok_or(Error::new(ESRCH))?, process.info) + ( + process + .threads + .first() + .ok_or(Error::new(ESRCH))? + .upgrade() + .ok_or(Error::new(ESRCH))?, + process.info, + ) }; if info.pgid != pgid { @@ -404,14 +438,23 @@ pub fn umask(mask: usize) -> Result { } fn reap(pid: ProcessId) -> Result { - let process_lock = Arc::clone(process::PROCESSES.read().get(&pid).ok_or(Error::new(ESRCH))?); + let process_lock = Arc::clone( + process::PROCESSES + .read() + .get(&pid) + .ok_or(Error::new(ESRCH))?, + ); // Spin until not running loop { // TODO: exit WaitCondition? { let mut process = process_lock.read(); - if process.threads.iter().all(|t| t.upgrade().map_or(true, |t| !t.read().running)) { + if process + .threads + .iter() + .all(|t| t.upgrade().map_or(true, |t| !t.read().running)) + { break; } } @@ -420,7 +463,8 @@ fn reap(pid: ProcessId) -> Result { interrupt::pause(); } - let _ = process::PROCESSES.write() + let _ = process::PROCESSES + .write() .remove(&pid) .ok_or(Error::new(ESRCH))?;