diff --git a/src/arch/x86_64/macros.rs b/src/arch/x86_64/macros.rs index 5ad34d8d4ce6d57d75f7bc599ecbb5d2a2eac6e7..dbbd424cbf89f61d3fda6ef87e951253bdfb68e1 100644 --- a/src/arch/x86_64/macros.rs +++ b/src/arch/x86_64/macros.rs @@ -281,7 +281,9 @@ impl InterruptStack { self.scratch.rcx = all.rcx; self.scratch.rax = all.rax; self.iret.rip = all.rip; - self.iret.cs = all.cs; + + // These should probably be restricted + // self.iret.cs = all.cs; // self.iret.rflags = all.eflags; } /// Enables the "Trap Flag" in the FLAGS register, causing the CPU diff --git a/src/context/signal.rs b/src/context/signal.rs index 942fb28c11ed80dc738e2b5098dc9b47c86d44c7..78e165af4f352453d7fcc7da46a5a5d223f00fe2 100644 --- a/src/context/signal.rs +++ b/src/context/signal.rs @@ -1,6 +1,5 @@ use alloc::sync::Arc; use core::mem; -use syscall::data::PtraceEvent; use syscall::flag::{PTRACE_FLAG_IGNORE, PTRACE_STOP_SIGNAL, SIG_DFL, SIG_IGN, SIGCHLD, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU}; use syscall::ptrace_event; diff --git a/src/lib.rs b/src/lib.rs index 24b265af194e42401320a04a2a3b7f421b610a60..b7acc714f8889e123a4172b1121853a9ce417207 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,6 @@ #![cfg_attr(feature = "clippy", allow(or_fun_call))] #![cfg_attr(feature = "clippy", allow(too_many_arguments))] #![deny(unreachable_patterns)] -#![feature(alloc)] #![feature(allocator_api)] #![feature(asm)] #![feature(concat_idents)] diff --git a/src/ptrace.rs b/src/ptrace.rs index c296922a6ab9e2c2927bccc69d36844236b1204f..b3631969823be1ef4af28b1dcc4e75e04c5256f1 100644 --- a/src/ptrace.rs +++ b/src/ptrace.rs @@ -209,8 +209,8 @@ pub fn set_breakpoint(pid: ContextId, flags: PtraceFlags) { /// Wait for the tracee to stop. If an event occurs, it returns a copy /// of that. It will still be available for read using recv_event. /// -/// Note: Don't call while holding any locks, this will switch -/// contexts +/// Note: Don't call while holding any locks or allocated data, this +/// will switch contexts and may in fact just never terminate. pub fn wait(pid: ContextId) -> Result<()> { let tracer: Arc<WaitCondition> = { let sessions = sessions(); @@ -238,7 +238,9 @@ pub fn wait(pid: ContextId) -> Result<()> { } /// Notify the tracer and await green flag to continue. -/// Note: Don't call while holding any locks, this will switch contexts +/// +/// Note: Don't call while holding any locks or allocated data, this +/// will switch contexts and may in fact just never terminate. pub fn breakpoint_callback(match_flags: PtraceFlags, event: Option<PtraceEvent>) -> Option<PtraceFlags> { // Can't hold any locks when executing wait() let (tracee, flags) = { @@ -422,8 +424,12 @@ pub unsafe fn regs_for_mut(context: &mut Context) -> Option<&mut InterruptStack> pub fn with_context_memory<F>(context: &Context, offset: VirtualAddress, len: usize, f: F) -> Result<()> where F: FnOnce(*mut u8) -> Result<()> { - // TODO: Is using USER_TMP_MISC_OFFSET safe? I guess make sure - // it's not too large. + // As far as I understand, mapping any regions following + // USER_TMP_MISC_OFFSET is safe because no other memory location + // is used after it. In the future it might be necessary to define + // a maximum amount of pages that can be mapped in one batch, + // which could be used to either internally retry `read`/`write` + // in `proc:<pid>/mem`, or return a partial read/write. let start = Page::containing_address(VirtualAddress::new(crate::USER_TMP_MISC_OFFSET)); let mut active_page_table = unsafe { ActivePageTable::new() }; diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs index 8d2e345ec789baf461ee660f931615507a120377..fbdaba622f5ee94e5676f8f0969f75ff94490cac 100644 --- a/src/scheme/mod.rs +++ b/src/scheme/mod.rs @@ -173,7 +173,7 @@ impl SchemeList { Ok(to) } - pub fn iter(&self) -> ::alloc::collections::btree_map::Iter<SchemeId, Arc<Box<Scheme + Send + Sync>>> { + pub fn iter(&self) -> ::alloc::collections::btree_map::Iter<SchemeId, Arc<Box<dyn Scheme + Send + Sync>>> { self.map.iter() } @@ -188,7 +188,7 @@ impl SchemeList { self.map.get(&id) } - pub fn get_name(&self, ns: SchemeNamespace, name: &[u8]) -> Option<(SchemeId, &Arc<Box<Scheme + Send + Sync>>)> { + pub fn get_name(&self, ns: SchemeNamespace, name: &[u8]) -> Option<(SchemeId, &Arc<Box<dyn Scheme + Send + Sync>>)> { if let Some(names) = self.names.get(&ns) { if let Some(&id) = names.get(name) { return self.get(id).map(|scheme| (id, scheme)); @@ -199,7 +199,7 @@ impl SchemeList { /// Create a new scheme. pub fn insert<F>(&mut self, ns: SchemeNamespace, name: Box<[u8]>, scheme_fn: F) -> Result<SchemeId> - where F: Fn(SchemeId) -> Arc<Box<Scheme + Send + Sync>> + where F: Fn(SchemeId) -> Arc<Box<dyn Scheme + Send + Sync>> { if let Some(names) = self.names.get(&ns) { if names.contains_key(&name) { diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index b91831a415ac39e41e18cab9ec3efc66ffa69caf..3b66635921796d36fb8c51f2b1d2d121bdd29a49 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -15,7 +15,6 @@ use crate::{ use alloc::{ collections::BTreeMap, - sync::Arc, vec::Vec }; use core::{ @@ -24,21 +23,7 @@ use core::{ slice, sync::atomic::{AtomicUsize, Ordering}, }; -use spin::{Mutex, RwLock}; - -#[derive(Clone, Copy)] -enum RegsKind { - Float, - Int -} -#[derive(Clone)] -enum Operation { - Memory(VirtualAddress), - Regs(RegsKind), - Trace { - clones: Vec<ContextId> - } -} +use spin::RwLock; fn with_context<F, T>(pid: ContextId, callback: F) -> Result<T> where F: FnOnce(&Context) -> Result<T> @@ -101,23 +86,75 @@ fn try_stop_context<F, T>(pid: ContextId, restart_after: bool, mut callback: F) } } +#[derive(Clone, Copy, PartialEq, Eq)] +enum RegsKind { + Float, + Int +} +#[derive(Clone, Copy, PartialEq, Eq)] +enum Operation { + Memory, + Regs(RegsKind), + Trace, +} +struct MemData { + offset: VirtualAddress, +} +impl Default for MemData { + fn default() -> Self { + Self { offset: VirtualAddress::new(0) } + } +} +#[derive(Default)] +struct TraceData { + clones: Vec<ContextId>, +} +enum OperationData { + Memory(MemData), + Trace(TraceData), + Other, +} +impl OperationData { + fn default_for(op: Operation) -> OperationData { + match op { + Operation::Memory => OperationData::Memory(MemData::default()), + Operation::Trace => OperationData::Trace(TraceData::default()), + _ => OperationData::Other, + } + } + fn trace_data(&mut self) -> Option<&mut TraceData> { + match self { + OperationData::Trace(data) => Some(data), + _ => None, + } + } + fn mem_data(&mut self) -> Option<&mut MemData> { + match self { + OperationData::Memory(data) => Some(data), + _ => None, + } + } +} + #[derive(Clone, Copy)] struct Info { pid: ContextId, flags: usize, + + // Important: Operation must never change. Search for: + // + // "operations can't change" to see usages. + operation: Operation, } struct Handle { info: Info, - operation: Operation + data: OperationData, } impl Handle { fn continue_ignored_children(&mut self) -> Option<()> { - let clones = match self.operation { - Operation::Trace { ref mut clones } => clones, - _ => return None - }; + let data = self.data.trace_data()?; let contexts = context::contexts(); - for pid in clones.drain(..) { + for pid in data.clones.drain(..) { if ptrace::is_traced(pid) { continue; } @@ -134,7 +171,7 @@ pub static PROC_SCHEME_ID: AtomicSchemeId = ATOMIC_SCHEMEID_INIT; pub struct ProcScheme { next_id: AtomicUsize, - handles: RwLock<BTreeMap<usize, Arc<Mutex<Handle>>>> + handles: RwLock<BTreeMap<usize, Handle>>, } impl ProcScheme { @@ -156,13 +193,12 @@ impl Scheme for ProcScheme { .and_then(|s| s.parse().ok()) .map(ContextId::from) .ok_or(Error::new(EINVAL))?; + let operation = match parts.next() { - Some("mem") => Operation::Memory(VirtualAddress::new(0)), + Some("mem") => Operation::Memory, Some("regs/float") => Operation::Regs(RegsKind::Float), Some("regs/int") => Operation::Regs(RegsKind::Int), - Some("trace") => Operation::Trace { - clones: Vec::new() - }, + Some("trace") => Operation::Trace, _ => return Err(Error::new(EINVAL)) }; @@ -215,13 +251,14 @@ impl Scheme for ProcScheme { } } - self.handles.write().insert(id, Arc::new(Mutex::new(Handle { + self.handles.write().insert(id, Handle { info: Info { flags, pid, + operation, }, - operation, - }))); + data: OperationData::default_for(operation), + }); Ok(id) } @@ -236,7 +273,6 @@ impl Scheme for ProcScheme { let info = { let handles = self.handles.read(); let handle = handles.get(&old_id).ok_or(Error::new(EBADF))?; - let handle = handle.lock(); handle.info }; @@ -254,45 +290,45 @@ impl Scheme for ProcScheme { } fn seek(&self, id: usize, pos: usize, whence: usize) -> Result<usize> { - let handles = self.handles.read(); - let handle = handles.get(&id).ok_or(Error::new(EBADF))?; - let mut handle = handle.lock(); - - match handle.operation { - Operation::Memory(ref mut offset) => Ok({ - *offset = VirtualAddress::new(match whence { - SEEK_SET => pos, - SEEK_CUR => cmp::max(0, offset.get() as isize + pos as isize) as usize, - SEEK_END => cmp::max(0, isize::max_value() + pos as isize) as usize, - _ => return Err(Error::new(EBADF)) - }); - offset.get() - }), - _ => Err(Error::new(EBADF)) - } + let mut handles = self.handles.write(); + let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let mut memory = handle.data.mem_data().ok_or(Error::new(EBADF))?; + + let value = match whence { + SEEK_SET => pos, + SEEK_CUR => cmp::max(0, memory.offset.get() as isize + pos as isize) as usize, + SEEK_END => cmp::max(0, isize::max_value() + pos as isize) as usize, + _ => return Err(Error::new(EBADF)) + }; + memory.offset = VirtualAddress::new(value); + Ok(value) } fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> { // Don't hold a global lock during the context switch later on - let handle = { + let info = { let handles = self.handles.read(); - Arc::clone(handles.get(&id).ok_or(Error::new(EBADF))?) + let handle = handles.get(&id).ok_or(Error::new(EBADF))?; + handle.info }; - let mut handle = handle.lock(); - let info = handle.info; - match handle.operation { - Operation::Memory(ref mut offset) => { + match info.operation { + Operation::Memory => { + // Won't context switch, don't worry about the locks + let mut handles = self.handles.write(); + let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let data = handle.data.mem_data().expect("operations can't change"); + let contexts = context::contexts(); let context = contexts.get(info.pid).ok_or(Error::new(ESRCH))?; let context = context.read(); - ptrace::with_context_memory(&context, *offset, buf.len(), |ptr| { + ptrace::with_context_memory(&context, data.offset, buf.len(), |ptr| { buf.copy_from_slice(validate::validate_slice(ptr, buf.len())?); Ok(()) })?; - *offset = VirtualAddress::new(offset.get() + buf.len()); + data.offset = VirtualAddress::new(data.offset.get() + buf.len()); Ok(buf.len()) }, Operation::Regs(kind) => { @@ -332,7 +368,7 @@ impl Scheme for ProcScheme { Ok(len) }, - Operation::Trace { ref mut clones } => { + Operation::Trace => { let slice = unsafe { slice::from_raw_parts_mut( buf.as_mut_ptr() as *mut PtraceEvent, @@ -341,9 +377,14 @@ impl Scheme for ProcScheme { }; let read = ptrace::recv_events(info.pid, slice).unwrap_or(0); + // Won't context switch, don't worry about the locks + let mut handles = self.handles.write(); + let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let data = handle.data.trace_data().expect("operations can't change"); + for event in &slice[..read] { if event.cause == PTRACE_EVENT_CLONE { - clones.push(ContextId::from(event.a)); + data.clones.push(ContextId::from(event.a)); } } @@ -354,26 +395,30 @@ impl Scheme for ProcScheme { fn write(&self, id: usize, buf: &[u8]) -> Result<usize> { // Don't hold a global lock during the context switch later on - let handle = { - let handles = self.handles.read(); - Arc::clone(handles.get(&id).ok_or(Error::new(EBADF))?) + let info = { + let mut handles = self.handles.write(); + let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + handle.continue_ignored_children(); + handle.info }; - let mut handle = handle.lock(); - let info = handle.info; - handle.continue_ignored_children(); - match handle.operation { - Operation::Memory(ref mut offset) => { + match info.operation { + Operation::Memory => { + // Won't context switch, don't worry about the locks + let mut handles = self.handles.write(); + let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let data = handle.data.mem_data().expect("operations can't change"); + let contexts = context::contexts(); let context = contexts.get(info.pid).ok_or(Error::new(ESRCH))?; let context = context.read(); - ptrace::with_context_memory(&context, *offset, buf.len(), |ptr| { + ptrace::with_context_memory(&context, data.offset, buf.len(), |ptr| { validate::validate_slice_mut(ptr, buf.len())?.copy_from_slice(buf); Ok(()) })?; - *offset = VirtualAddress::new(offset.get() + buf.len()); + data.offset = VirtualAddress::new(data.offset.get() + buf.len()); Ok(buf.len()) }, Operation::Regs(kind) => match kind { @@ -416,7 +461,7 @@ impl Scheme for ProcScheme { }) } }, - Operation::Trace { .. } => { + Operation::Trace => { if buf.len() < mem::size_of::<u64>() { return Ok(0); } @@ -470,9 +515,8 @@ impl Scheme for ProcScheme { } fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result<usize> { - let handles = self.handles.read(); - let handle = handles.get(&id).ok_or(Error::new(EBADF))?; - let mut handle = handle.lock(); + let mut handles = self.handles.write(); + let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; match cmd { F_SETFL => { handle.info.flags = arg; Ok(0) }, @@ -484,7 +528,6 @@ impl Scheme for ProcScheme { fn fevent(&self, id: usize, _flags: EventFlags) -> Result<EventFlags> { let handles = self.handles.read(); let handle = handles.get(&id).ok_or(Error::new(EBADF))?; - let handle = handle.lock(); Ok(ptrace::session_fevent_flags(handle.info.pid).expect("proc (fevent): invalid session")) } @@ -492,13 +535,12 @@ impl Scheme for ProcScheme { fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> { let handles = self.handles.read(); let handle = handles.get(&id).ok_or(Error::new(EBADF))?; - let handle = handle.lock(); - let path = format!("proc:{}/{}", handle.info.pid.into(), match handle.operation { - Operation::Memory(_) => "mem", + let path = format!("proc:{}/{}", handle.info.pid.into(), match handle.info.operation { + Operation::Memory => "mem", Operation::Regs(RegsKind::Float) => "regs/float", Operation::Regs(RegsKind::Int) => "regs/int", - Operation::Trace { .. } => "trace" + Operation::Trace => "trace" }); let len = cmp::min(path.len(), buf.len()); @@ -508,11 +550,10 @@ impl Scheme for ProcScheme { } fn close(&self, id: usize) -> Result<usize> { - let handle = self.handles.write().remove(&id).ok_or(Error::new(EBADF))?; - let mut handle = handle.lock(); + let mut handle = self.handles.write().remove(&id).ok_or(Error::new(EBADF))?; handle.continue_ignored_children(); - if let Operation::Trace { .. } = handle.operation { + if let Operation::Trace = handle.info.operation { ptrace::close_session(handle.info.pid); if handle.info.flags & O_EXCL == O_EXCL { diff --git a/src/scheme/sys/mod.rs b/src/scheme/sys/mod.rs index ee995117e14df6b4e7148714166fe510269d73ad..d12729d7bb8008e6de42075b7338a7ddc53b694d 100644 --- a/src/scheme/sys/mod.rs +++ b/src/scheme/sys/mod.rs @@ -27,7 +27,7 @@ struct Handle { seek: usize } -type SysFn = Fn() -> Result<Vec<u8>> + Send + Sync; +type SysFn = dyn Fn() -> Result<Vec<u8>> + Send + Sync; /// System information scheme pub struct SysScheme { diff --git a/src/syscall/process.rs b/src/syscall/process.rs index 646db9a1a1300cbed5966aea78abdb3fc3c36cf4..45baf3b1d7ecae75e2af6e9081117de399c80f06 100644 --- a/src/syscall/process.rs +++ b/src/syscall/process.rs @@ -21,12 +21,13 @@ use crate::paging::{ActivePageTable, InactivePageTable, Page, VirtualAddress, PA use crate::{ptrace, syscall}; use crate::scheme::FileHandle; use crate::start::usermode; -use crate::syscall::data::{PtraceEvent, SigAction, Stat}; +use crate::syscall::data::{SigAction, Stat}; use crate::syscall::error::*; use crate::syscall::flag::{CloneFlags, CLONE_VFORK, CLONE_VM, CLONE_FS, CLONE_FILES, CLONE_SIGHAND, CLONE_STACK, MapFlags, PROT_EXEC, PROT_READ, PROT_WRITE, PTRACE_EVENT_CLONE, - SigActionFlags, SIG_DFL, SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK, SIGCONT, SIGTERM, - WaitFlags, WCONTINUED, WNOHANG, WUNTRACED, wifcontinued, wifstopped}; + PTRACE_STOP_EXIT, SigActionFlags, SIG_DFL, SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK, + SIGCONT, SIGTERM, WaitFlags, WCONTINUED, WNOHANG, WUNTRACED, wifcontinued, + wifstopped}; use crate::syscall::ptrace_event; use crate::syscall::validate::{validate_slice, validate_slice_mut}; @@ -1064,6 +1065,8 @@ pub fn exit(status: usize) -> ! { Arc::clone(&context_lock) }; + ptrace::breakpoint_callback(PTRACE_STOP_EXIT, Some(ptrace_event!(PTRACE_STOP_EXIT, status))); + let mut close_files = Vec::new(); let pid = { let mut context = context_lock.write(); diff --git a/syscall b/syscall index 0e1e7d5d36f8c6f045e30c37515d366350eb2122..6ba71e7e065d37309b7d5c4fc86835dd350f61b7 160000 --- a/syscall +++ b/syscall @@ -1 +1 @@ -Subproject commit 0e1e7d5d36f8c6f045e30c37515d366350eb2122 +Subproject commit 6ba71e7e065d37309b7d5c4fc86835dd350f61b7