From 12f632837aaea242d57bd122724b9256fbcc31a5 Mon Sep 17 00:00:00 2001 From: jD91mZM2 Date: Tue, 16 Jun 2020 10:07:41 +0200 Subject: [PATCH] Misc proc code cleanup --- src/ptrace.rs | 167 +++++++++++++++++------------------------ src/scheme/proc.rs | 49 +++++++----- src/syscall/process.rs | 4 +- 3 files changed, 100 insertions(+), 120 deletions(-) diff --git a/src/ptrace.rs b/src/ptrace.rs index 5ae54f4..b55b3f6 100644 --- a/src/ptrace.rs +++ b/src/ptrace.rs @@ -48,7 +48,7 @@ use spin::{Mutex, Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; // |____/ \___||___/___/_|\___/|_| |_|___/ #[derive(Debug)] -struct SessionData { +pub struct SessionData { breakpoint: Option, events: VecDeque, file_id: usize, @@ -63,13 +63,53 @@ impl SessionData { proc_trigger_event(self.file_id, EVENT_READ); } } + + /// Override the breakpoint for the specified tracee. Pass `None` to clear + /// breakpoint. + pub fn set_breakpoint(&mut self, flags: Option) { + self.breakpoint = flags.map(|flags| Breakpoint { + reached: false, + flags + }); + } + + /// Used for getting the flags in fevent + pub fn session_fevent_flags(&self) -> EventFlags { + let mut flags = EventFlags::empty(); + + if !self.events.is_empty() { + flags |= EVENT_READ; + } + + flags + } + + /// Poll events, return the amount read. This drains events from the queue. + pub fn recv_events(&mut self, out: &mut [PtraceEvent]) -> usize { + let len = cmp::min(out.len(), self.events.len()); + for (dst, src) in out.iter_mut().zip(self.events.drain(..len)) { + *dst = src; + } + len + } } #[derive(Debug)] -struct Session { - data: Mutex, - tracee: WaitCondition, - tracer: WaitCondition, +pub struct Session { + pub data: Mutex, + pub tracee: WaitCondition, + pub tracer: WaitCondition, +} +impl Session { + pub fn with_session(pid: ContextId, callback: F) -> Result + where + F: FnOnce(&Session) -> Result, + { + let sessions = sessions(); + let session = sessions.get(&pid).ok_or(Error::new(ENODEV))?; + + callback(session) + } } type SessionMap = BTreeMap>; @@ -108,33 +148,23 @@ pub fn try_new_session(pid: ContextId, file_id: usize) -> bool { } } -/// Returns true if a session is attached to this process -pub fn is_traced(pid: ContextId) -> bool { - sessions().contains_key(&pid) -} - -/// Used for getting the flags in fevent -pub fn session_fevent_flags(pid: ContextId) -> Option { - let sessions = sessions(); - let session = sessions.get(&pid)?; - let data = session.data.lock(); - - let mut flags = EventFlags::empty(); - if !data.events.is_empty() { - flags |= EVENT_READ; - } - Some(flags) -} - /// Remove the session from the list of open sessions and notify any /// waiting processes pub fn close_session(pid: ContextId) { if let Some(session) = sessions_mut().remove(&pid) { session.tracer.notify(); session.tracee.notify(); + + let data = session.data.lock(); + proc_trigger_event(data.file_id, EVENT_READ); } } +/// Returns true if a session is attached to this process +pub fn is_traced(pid: ContextId) -> bool { + sessions().contains_key(&pid) +} + /// Trigger a notification to the event: scheme fn proc_trigger_event(file_id: usize, flags: EventFlags) { event::trigger(proc::PROC_SCHEME_ID.load(Ordering::SeqCst), file_id, flags); @@ -144,12 +174,15 @@ fn proc_trigger_event(file_id: usize, flags: EventFlags) { /// the tracer to wake up and poll for events. Returns Some(()) if an /// event was sent. pub fn send_event(event: PtraceEvent) -> Option<()> { - let contexts = context::contexts(); - let context = contexts.current()?; - let context = context.read(); + let id = { + let contexts = context::contexts(); + let context = contexts.current()?; + let context = context.read(); + context.id + }; let sessions = sessions(); - let session = sessions.get(&context.id)?; + let session = sessions.get(&id)?; let mut data = session.data.lock(); let breakpoint = data.breakpoint.as_ref()?; @@ -165,19 +198,6 @@ pub fn send_event(event: PtraceEvent) -> Option<()> { Some(()) } -/// Poll events, return the amount read -pub fn recv_events(pid: ContextId, out: &mut [PtraceEvent]) -> Option { - let mut sessions = sessions_mut(); - let session = sessions.get_mut(&pid)?; - let mut data = session.data.lock(); - - let len = cmp::min(out.len(), data.events.len()); - for (dst, src) in out.iter_mut().zip(data.events.drain(..len)) { - *dst = src; - } - Some(len) -} - // ____ _ _ _ // | __ ) _ __ ___ __ _| | ___ __ ___ (_)_ __ | |_ ___ // | _ \| '__/ _ \/ _` | |/ / '_ \ / _ \| | '_ \| __/ __| @@ -191,49 +211,11 @@ struct Breakpoint { flags: PtraceFlags } -/// Clear any breakpoints for the process with the specified ID -pub fn clear_breakpoint(pid: ContextId) { - let sessions = sessions(); - let session = match sessions.get(&pid) { - Some(session) => session, - None => return - }; - let mut data = session.data.lock(); - - // Remove the breakpoint to make sure any yet unreached but - // obsolete breakpoints don't stop the program. - data.breakpoint = None; -} - -/// Notify the tracee of the current session. Returns None if session does not exist -pub fn notify_tracee(pid: ContextId) { - let sessions = sessions(); - let session = match sessions.get(&pid) { - Some(session) => session, - None => return - }; - - session.tracee.notify(); -} - -/// Create a new breakpoint for the specified tracee, optionally with -/// a sysemu flag. Panics if the session is invalid. -pub fn set_breakpoint(pid: ContextId, flags: PtraceFlags) { - let sessions = sessions_mut(); - let session = sessions.get(&pid).expect("proc (set_breakpoint): invalid session"); - let mut data = session.data.lock(); - - data.breakpoint = Some(Breakpoint { - reached: false, - flags - }); -} - -/// 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. +/// Wait for the tracee to stop, or return immediately if there's an unread +/// event. /// -/// Note: Don't call while holding any locks or allocated data, this -/// will switch contexts and may in fact just never terminate. +/// 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<()> { loop { let session = { @@ -272,7 +254,9 @@ pub fn wait(pid: ContextId) -> Result<()> { Ok(()) } -/// Notify the tracer and await green flag to continue. +/// Notify the tracer and await green flag to continue. If the breakpoint was +/// set and reached, return the flags which the user waited for. Otherwise, +/// None. /// /// Note: Don't call while holding any locks or allocated data, this /// will switch contexts and may in fact just never terminate. @@ -317,8 +301,6 @@ pub fn breakpoint_callback(match_flags: PtraceFlags, event: Option) /// Obtain the next breakpoint flags for the current process. This is used for /// detecting whether or not the tracer decided to use sysemu mode. -// TODO: Check if this is actually safe from race conditions, maybe it -// shouldn't just drop the locks like this... pub fn next_breakpoint() -> Option { let contexts = context::contexts(); let context = contexts.current()?; @@ -332,21 +314,6 @@ pub fn next_breakpoint() -> Option { Some(breakpoint.flags) } -/// Call when a context is closed to alert any tracers -pub fn close_tracee(pid: ContextId) -> Option<()> { - let sessions = sessions(); - let session = sessions.get(&pid)?; - let mut data = session.data.lock(); - - // Cause tracers to wake up. Any following action from the tracer will - // return ESRCH which can be used to detect exit. - data.breakpoint = None; - proc_trigger_event(data.file_id, EVENT_READ); - session.tracer.notify(); - - Some(()) -} - // ____ _ _ // | _ \ ___ __ _(_)___| |_ ___ _ __ ___ // | |_) / _ \/ _` | / __| __/ _ \ '__/ __| diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index 5b53851..37f640d 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -26,7 +26,8 @@ use core::{ use spin::RwLock; fn with_context(pid: ContextId, callback: F) -> Result - where F: FnOnce(&Context) -> Result +where + F: FnOnce(&Context) -> Result, { let contexts = context::contexts(); let context = contexts.get(pid).ok_or(Error::new(ESRCH))?; @@ -37,7 +38,8 @@ fn with_context(pid: ContextId, callback: F) -> Result callback(&context) } fn with_context_mut(pid: ContextId, callback: F) -> Result - where F: FnOnce(&mut Context) -> Result +where + F: FnOnce(&mut Context) -> Result, { let contexts = context::contexts(); let context = contexts.get(pid).ok_or(Error::new(ESRCH))?; @@ -369,7 +371,9 @@ impl Scheme for ProcScheme { buf.len() / mem::size_of::() ) }; - let read = ptrace::recv_events(info.pid, slice).unwrap_or(0); + let read = ptrace::Session::with_session(info.pid, |session| { + Ok(session.data.lock().recv_events(slice)) + })?; // Won't context switch, don't worry about the locks let mut handles = self.handles.write(); @@ -468,12 +472,15 @@ impl Scheme for ProcScheme { let should_continue = !op.contains(PTRACE_FLAG_WAIT) || op.intersects(PTRACE_STOP_MASK); - // Set next breakpoint, or clear it if no stop condition was set and we should continue - if op.intersects(PTRACE_STOP_MASK) { - ptrace::set_breakpoint(info.pid, op); - } else if should_continue { - ptrace::clear_breakpoint(info.pid); - } + // Set next breakpoint + ptrace::Session::with_session(info.pid, |session| { + if op.intersects(PTRACE_STOP_MASK) { + session.data.lock().set_breakpoint(Some(op)); + } else if should_continue { + session.data.lock().set_breakpoint(None); + } + Ok(()) + })?; if op.contains(PTRACE_STOP_SINGLESTEP) { try_stop_context(info.pid, |context| { @@ -490,6 +497,7 @@ impl Scheme for ProcScheme { })?; } + // Continue execution, if requested if should_continue { // disable the ptrace_stop flag, which is used in some cases with_context_mut(info.pid, |context| { @@ -498,10 +506,13 @@ impl Scheme for ProcScheme { })?; // and notify the tracee's WaitCondition, which is used in other cases - ptrace::notify_tracee(info.pid); + ptrace::Session::with_session(info.pid, |session| { + session.tracee.notify(); + Ok(()) + })?; } - // And await the tracee, if requested to + // And await the tracee, if requested if op.contains(PTRACE_FLAG_WAIT) || info.flags & O_NONBLOCK != O_NONBLOCK { ptrace::wait(info.pid)?; } @@ -526,7 +537,9 @@ impl Scheme for ProcScheme { let handles = self.handles.read(); let handle = handles.get(&id).ok_or(Error::new(EBADF))?; - Ok(ptrace::session_fevent_flags(handle.info.pid).expect("proc (fevent): invalid session")) + ptrace::Session::with_session(handle.info.pid, |session| { + Ok(session.data.lock().session_fevent_flags()) + }) } fn fpath(&self, id: usize, buf: &mut [u8]) -> Result { @@ -555,12 +568,12 @@ impl Scheme for ProcScheme { if handle.info.flags & O_EXCL == O_EXCL { syscall::kill(handle.info.pid, SIGKILL)?; - } else { - let contexts = context::contexts(); - if let Some(context) = contexts.get(handle.info.pid) { - let mut context = context.write(); - context.ptrace_stop = false; - } + } + + let contexts = context::contexts(); + if let Some(context) = contexts.get(handle.info.pid) { + let mut context = context.write(); + context.ptrace_stop = false; } } Ok(0) diff --git a/src/syscall/process.rs b/src/syscall/process.rs index 1920976..cf1737b 100644 --- a/src/syscall/process.rs +++ b/src/syscall/process.rs @@ -1151,8 +1151,8 @@ pub fn exit(status: usize) -> ! { } } - // Alert any tracers waiting for process (important: AFTER sending waitpid event) - ptrace::close_tracee(pid); + // Alert any tracers waiting of this process + ptrace::close_session(pid); if pid == ContextId::from(1) { println!("Main kernel thread exited with status {:X}", status);