WIP(ptrace): Add ptrace event system for catching child forks
This commit is contained in:
243
src/ptrace.rs
243
src/ptrace.rs
@@ -10,17 +10,150 @@ use crate::{
|
||||
},
|
||||
common::unique::Unique,
|
||||
context::{self, Context, ContextId, Status},
|
||||
event,
|
||||
scheme::proc,
|
||||
sync::WaitCondition
|
||||
};
|
||||
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
collections::BTreeMap,
|
||||
collections::{
|
||||
BTreeMap,
|
||||
VecDeque,
|
||||
btree_map::Entry
|
||||
},
|
||||
sync::Arc,
|
||||
vec::Vec
|
||||
};
|
||||
use core::{
|
||||
cmp,
|
||||
sync::atomic::Ordering
|
||||
};
|
||||
use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
use syscall::error::*;
|
||||
use syscall::{
|
||||
data::PtraceEvent,
|
||||
error::*,
|
||||
flag::{EVENT_READ, EVENT_WRITE}
|
||||
};
|
||||
|
||||
// ____ _
|
||||
// / ___| ___ ___ ___(_) ___ _ __ ___
|
||||
// \___ \ / _ \/ __/ __| |/ _ \| '_ \/ __|
|
||||
// ___) | __/\__ \__ \ | (_) | | | \__ \
|
||||
// |____/ \___||___/___/_|\___/|_| |_|___/
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Session {
|
||||
file_id: usize,
|
||||
events: VecDeque<PtraceEvent>,
|
||||
breakpoint: Option<Breakpoint>,
|
||||
tracer: Arc<WaitCondition>
|
||||
}
|
||||
|
||||
type SessionMap = BTreeMap<ContextId, Session>;
|
||||
|
||||
static SESSIONS: Once<RwLock<SessionMap>> = Once::new();
|
||||
|
||||
fn init_sessions() -> RwLock<SessionMap> {
|
||||
RwLock::new(BTreeMap::new())
|
||||
}
|
||||
fn sessions() -> RwLockReadGuard<'static, SessionMap> {
|
||||
SESSIONS.call_once(init_sessions).read()
|
||||
}
|
||||
fn sessions_mut() -> RwLockWriteGuard<'static, SessionMap> {
|
||||
SESSIONS.call_once(init_sessions).write()
|
||||
}
|
||||
|
||||
/// Try to create a new session, but fail if one already exists for
|
||||
/// this process
|
||||
pub fn try_new_session(pid: ContextId, file_id: usize) -> bool {
|
||||
let mut sessions = sessions_mut();
|
||||
|
||||
match sessions.entry(pid) {
|
||||
Entry::Occupied(_) => false,
|
||||
Entry::Vacant(vacant) => {
|
||||
vacant.insert(Session {
|
||||
file_id,
|
||||
events: VecDeque::new(),
|
||||
breakpoint: None,
|
||||
tracer: Arc::new(WaitCondition::new())
|
||||
});
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<usize> {
|
||||
let sessions = sessions();
|
||||
let session = sessions.get(&pid)?;
|
||||
let mut flags = 0;
|
||||
if !session.events.is_empty() {
|
||||
flags |= EVENT_READ;
|
||||
}
|
||||
if session.breakpoint.as_ref().map(|b| b.reached).unwrap_or(true) {
|
||||
flags |= EVENT_WRITE;
|
||||
}
|
||||
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();
|
||||
if let Some(breakpoint) = session.breakpoint {
|
||||
breakpoint.tracee.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trigger a notification to the event: scheme
|
||||
pub fn proc_trigger_event(file_id: usize, flags: usize) {
|
||||
event::trigger(proc::PROC_SCHEME_ID.load(Ordering::SeqCst), file_id, flags);
|
||||
}
|
||||
|
||||
/// Dispatch an event to any tracer tracing `self`. This will cause
|
||||
/// 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 mut sessions = sessions_mut();
|
||||
let session = sessions.get_mut(&context.id)?;
|
||||
|
||||
session.events.push_back(event);
|
||||
|
||||
// Notify nonblocking tracers
|
||||
if session.events.len() == 1 {
|
||||
// If the list of events was previously empty, alert now
|
||||
proc_trigger_event(session.file_id, EVENT_READ);
|
||||
}
|
||||
|
||||
// Alert blocking tracers
|
||||
session.tracer.notify();
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
/// Poll events, return the amount read
|
||||
pub fn recv_events(pid: ContextId, out: &mut [PtraceEvent]) -> Option<usize> {
|
||||
let mut sessions = sessions_mut();
|
||||
let session = sessions.get_mut(&pid)?;
|
||||
|
||||
let len = cmp::min(out.len(), session.events.len());
|
||||
for (dst, src) in out.iter_mut().zip(session.events.drain(..len)) {
|
||||
*dst = src;
|
||||
}
|
||||
Some(len)
|
||||
}
|
||||
|
||||
// ____ _ _ _
|
||||
// | __ ) _ __ ___ __ _| | ___ __ ___ (_)_ __ | |_ ___
|
||||
@@ -29,33 +162,25 @@ use syscall::error::*;
|
||||
// |____/|_| \___|\__,_|_|\_\ .__/ \___/|_|_| |_|\__|___/
|
||||
// |_|
|
||||
|
||||
struct Handle {
|
||||
#[derive(Debug)]
|
||||
struct Breakpoint {
|
||||
tracee: Arc<WaitCondition>,
|
||||
tracer: Arc<WaitCondition>,
|
||||
reached: bool,
|
||||
|
||||
sysemu: bool,
|
||||
singlestep: bool
|
||||
}
|
||||
|
||||
static BREAKPOINTS: Once<RwLock<BTreeMap<ContextId, Handle>>> = Once::new();
|
||||
|
||||
fn init_breakpoints() -> RwLock<BTreeMap<ContextId, Handle>> {
|
||||
RwLock::new(BTreeMap::new())
|
||||
}
|
||||
fn breakpoints() -> RwLockReadGuard<'static, BTreeMap<ContextId, Handle>> {
|
||||
BREAKPOINTS.call_once(init_breakpoints).read()
|
||||
}
|
||||
fn breakpoints_mut() -> RwLockWriteGuard<'static, BTreeMap<ContextId, Handle>> {
|
||||
BREAKPOINTS.call_once(init_breakpoints).write()
|
||||
}
|
||||
|
||||
fn inner_cont(pid: ContextId) -> Option<Handle> {
|
||||
fn inner_cont(pid: ContextId) -> Option<Breakpoint> {
|
||||
// Remove the breakpoint to both save space and also make sure any
|
||||
// yet unreached but obsolete breakpoints don't stop the program.
|
||||
let handle = breakpoints_mut().remove(&pid)?;
|
||||
handle.tracee.notify();
|
||||
Some(handle)
|
||||
let mut sessions = sessions_mut();
|
||||
let session = sessions.get_mut(&pid)?;
|
||||
let breakpoint = session.breakpoint.take()?;
|
||||
|
||||
breakpoint.tracee.notify();
|
||||
|
||||
Some(breakpoint)
|
||||
}
|
||||
|
||||
/// Continue the process with the specified ID
|
||||
@@ -63,44 +188,61 @@ pub fn cont(pid: ContextId) {
|
||||
inner_cont(pid);
|
||||
}
|
||||
|
||||
/// Create a new breakpoint for the specified tracee, optionally with a sysemu flag
|
||||
/// 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, sysemu: bool, singlestep: bool) {
|
||||
let (tracee, tracer) = match inner_cont(pid) {
|
||||
Some(breakpoint) => (breakpoint.tracee, breakpoint.tracer),
|
||||
None => (
|
||||
Arc::new(WaitCondition::new()),
|
||||
Arc::new(WaitCondition::new())
|
||||
)
|
||||
};
|
||||
let tracee = inner_cont(pid)
|
||||
.map(|b| b.tracee)
|
||||
.unwrap_or_else(|| Arc::new(WaitCondition::new()));
|
||||
|
||||
breakpoints_mut().insert(pid, Handle {
|
||||
let mut sessions = sessions_mut();
|
||||
let session = sessions.get_mut(&pid).expect("proc (set_breakpoint): invalid session");
|
||||
session.breakpoint = Some(Breakpoint {
|
||||
tracee,
|
||||
tracer,
|
||||
reached: false,
|
||||
sysemu,
|
||||
singlestep
|
||||
});
|
||||
}
|
||||
|
||||
/// Wait for the tracee to stop.
|
||||
/// Note: Don't call while holding any locks, this will switch contexts
|
||||
pub fn wait_breakpoint(pid: ContextId) -> Result<()> {
|
||||
let tracer = {
|
||||
let breakpoints = breakpoints();
|
||||
match breakpoints.get(&pid) {
|
||||
Some(breakpoint) if !breakpoint.reached => Arc::clone(&breakpoint.tracer),
|
||||
_ => return Ok(())
|
||||
/// 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
|
||||
pub fn wait(pid: ContextId) -> Result<Option<PtraceEvent>> {
|
||||
let tracer: Arc<WaitCondition> = {
|
||||
let sessions = sessions();
|
||||
match sessions.get(&pid) {
|
||||
Some(session) if session.breakpoint.as_ref().map(|b| !b.reached).unwrap_or(true) => {
|
||||
if let Some(event) = session.events.front() {
|
||||
return Ok(Some(event.clone()));
|
||||
}
|
||||
Arc::clone(&session.tracer)
|
||||
},
|
||||
_ => return Ok(None)
|
||||
}
|
||||
};
|
||||
|
||||
while !tracer.wait() {}
|
||||
|
||||
{
|
||||
let sessions = sessions();
|
||||
if let Some(session) = sessions.get(&pid) {
|
||||
if let Some(event) = session.events.front() {
|
||||
return Ok(Some(event.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let contexts = context::contexts();
|
||||
let context = contexts.get(pid).ok_or(Error::new(ESRCH))?;
|
||||
let context = context.read();
|
||||
if let Status::Exited(_) = context.status {
|
||||
return Err(Error::new(ESRCH));
|
||||
}
|
||||
Ok(())
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Notify the tracer and await green flag to continue.
|
||||
@@ -112,8 +254,9 @@ pub fn breakpoint_callback(singlestep: bool) -> Option<bool> {
|
||||
let context = contexts.current()?;
|
||||
let context = context.read();
|
||||
|
||||
let mut breakpoints = breakpoints_mut();
|
||||
let breakpoint = breakpoints.get_mut(&context.id)?;
|
||||
let mut sessions = sessions_mut();
|
||||
let session = sessions.get_mut(&context.id)?;
|
||||
let breakpoint = session.breakpoint.as_mut()?;
|
||||
|
||||
// TODO: How should singlesteps interact with syscalls? How
|
||||
// does Linux handle this?
|
||||
@@ -123,11 +266,13 @@ pub fn breakpoint_callback(singlestep: bool) -> Option<bool> {
|
||||
return None;
|
||||
}
|
||||
|
||||
breakpoint.tracer.notify();
|
||||
// In case no tracer is waiting, make sure the next one gets
|
||||
// the memo
|
||||
breakpoint.reached = true;
|
||||
|
||||
session.tracer.notify();
|
||||
proc_trigger_event(session.file_id, EVENT_WRITE);
|
||||
|
||||
(
|
||||
Arc::clone(&breakpoint.tracee),
|
||||
breakpoint.sysemu
|
||||
@@ -140,15 +285,13 @@ pub fn breakpoint_callback(singlestep: bool) -> Option<bool> {
|
||||
}
|
||||
|
||||
/// Call when a context is closed to alert any tracers
|
||||
pub fn close(pid: ContextId) {
|
||||
{
|
||||
let breakpoints = breakpoints();
|
||||
if let Some(breakpoint) = breakpoints.get(&pid) {
|
||||
breakpoint.tracer.notify();
|
||||
}
|
||||
}
|
||||
pub fn close_tracee(pid: ContextId) -> Option<()> {
|
||||
let mut sessions = sessions_mut();
|
||||
let session = sessions.get_mut(&pid)?;
|
||||
|
||||
breakpoints_mut().remove(&pid);
|
||||
session.breakpoint = None;
|
||||
session.tracer.notify();
|
||||
Some(())
|
||||
}
|
||||
|
||||
// ____ _ _
|
||||
|
||||
@@ -96,7 +96,7 @@ impl<'a> Iterator for SchemeIter<'a> {
|
||||
|
||||
/// Scheme list type
|
||||
pub struct SchemeList {
|
||||
map: BTreeMap<SchemeId, Arc<Box<Scheme + Send + Sync>>>,
|
||||
map: BTreeMap<SchemeId, Arc<Box<dyn Scheme + Send + Sync>>>,
|
||||
names: BTreeMap<SchemeNamespace, BTreeMap<Box<[u8]>, SchemeId>>,
|
||||
next_ns: usize,
|
||||
next_id: usize
|
||||
@@ -141,7 +141,7 @@ impl SchemeList {
|
||||
self.insert(ns, Box::new(*b"debug"), |scheme_id| Arc::new(Box::new(DebugScheme::new(scheme_id)))).unwrap();
|
||||
self.insert(ns, Box::new(*b"initfs"), |_| Arc::new(Box::new(InitFsScheme::new()))).unwrap();
|
||||
self.insert(ns, Box::new(*b"irq"), |scheme_id| Arc::new(Box::new(IrqScheme::new(scheme_id)))).unwrap();
|
||||
self.insert(ns, Box::new(*b"proc"), |_| Arc::new(Box::new(ProcScheme::new()))).unwrap();
|
||||
self.insert(ns, Box::new(*b"proc"), |scheme_id| Arc::new(Box::new(ProcScheme::new(scheme_id)))).unwrap();
|
||||
|
||||
#[cfg(feature = "live")] {
|
||||
self.insert(ns, Box::new(*b"disk/live"), |_| Arc::new(Box::new(self::live::DiskScheme::new()))).unwrap();
|
||||
@@ -184,7 +184,7 @@ impl SchemeList {
|
||||
}
|
||||
|
||||
/// Get the nth scheme.
|
||||
pub fn get(&self, id: SchemeId) -> Option<&Arc<Box<Scheme + Send + Sync>>> {
|
||||
pub fn get(&self, id: SchemeId) -> Option<&Arc<Box<dyn Scheme + Send + Sync>>> {
|
||||
self.map.get(&id)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use crate::{
|
||||
arch::paging::VirtualAddress,
|
||||
context::{self, ContextId, Status},
|
||||
syscall::validate,
|
||||
ptrace
|
||||
ptrace,
|
||||
scheme::{ATOMIC_SCHEMEID_INIT, AtomicSchemeId, SchemeId},
|
||||
syscall::validate
|
||||
};
|
||||
|
||||
use alloc::{
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
collections::BTreeMap,
|
||||
sync::Arc
|
||||
};
|
||||
use core::{
|
||||
@@ -17,7 +18,7 @@ use core::{
|
||||
};
|
||||
use spin::{Mutex, RwLock};
|
||||
use syscall::{
|
||||
data::{IntRegisters, FloatRegisters},
|
||||
data::{FloatRegisters, IntRegisters, PtraceEvent},
|
||||
error::*,
|
||||
flag::*,
|
||||
scheme::Scheme
|
||||
@@ -32,7 +33,9 @@ enum RegsKind {
|
||||
enum Operation {
|
||||
Memory(VirtualAddress),
|
||||
Regs(RegsKind),
|
||||
Trace
|
||||
Trace {
|
||||
new_child: Option<ContextId>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -41,19 +44,37 @@ struct Handle {
|
||||
pid: ContextId,
|
||||
operation: Operation
|
||||
}
|
||||
impl Handle {
|
||||
fn continue_ignored_child(&mut self) -> Option<()> {
|
||||
let pid = match self.operation {
|
||||
Operation::Trace { ref mut new_child } => new_child.take()?,
|
||||
_ => return None
|
||||
};
|
||||
if ptrace::is_traced(pid) {
|
||||
return None;
|
||||
}
|
||||
let contexts = context::contexts();
|
||||
let context = contexts.get(pid)?;
|
||||
let mut context = context.write();
|
||||
context.ptrace_stop = false;
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
pub static PROC_SCHEME_ID: AtomicSchemeId = ATOMIC_SCHEMEID_INIT;
|
||||
|
||||
pub struct ProcScheme {
|
||||
next_id: AtomicUsize,
|
||||
handles: RwLock<BTreeMap<usize, Arc<Mutex<Handle>>>>,
|
||||
traced: Mutex<BTreeSet<ContextId>>
|
||||
handles: RwLock<BTreeMap<usize, Arc<Mutex<Handle>>>>
|
||||
}
|
||||
|
||||
impl ProcScheme {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(scheme_id: SchemeId) -> Self {
|
||||
PROC_SCHEME_ID.store(scheme_id, Ordering::SeqCst);
|
||||
|
||||
Self {
|
||||
next_id: AtomicUsize::new(0),
|
||||
handles: RwLock::new(BTreeMap::new()),
|
||||
traced: Mutex::new(BTreeSet::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,50 +91,59 @@ impl Scheme for ProcScheme {
|
||||
Some("mem") => Operation::Memory(VirtualAddress::new(0)),
|
||||
Some("regs/float") => Operation::Regs(RegsKind::Float),
|
||||
Some("regs/int") => Operation::Regs(RegsKind::Int),
|
||||
Some("trace") => Operation::Trace,
|
||||
Some("trace") => Operation::Trace {
|
||||
new_child: None
|
||||
},
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
};
|
||||
|
||||
let contexts = context::contexts();
|
||||
let target = contexts.get(pid).ok_or(Error::new(ESRCH))?;
|
||||
|
||||
// Unless root, check security
|
||||
if uid != 0 && gid != 0 {
|
||||
let current = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let current = current.read();
|
||||
{
|
||||
let target = target.read();
|
||||
|
||||
// Do we own the process?
|
||||
if uid != target.euid && gid != target.egid {
|
||||
return Err(Error::new(EPERM));
|
||||
if let Status::Exited(_) = target.status {
|
||||
return Err(Error::new(ESRCH));
|
||||
}
|
||||
|
||||
// Is it a subprocess of us? In the future, a capability
|
||||
// could bypass this check.
|
||||
match contexts.anchestors(target.ppid).find(|&(id, _context)| id == current.id) {
|
||||
Some((id, context)) => {
|
||||
// Paranoid sanity check, as ptrace security holes
|
||||
// wouldn't be fun
|
||||
assert_eq!(id, current.id);
|
||||
assert_eq!(id, context.read().id);
|
||||
},
|
||||
None => return Err(Error::new(EPERM))
|
||||
// Unless root, check security
|
||||
if uid != 0 && gid != 0 {
|
||||
let current = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let current = current.read();
|
||||
|
||||
// Do we own the process?
|
||||
if uid != target.euid && gid != target.egid {
|
||||
return Err(Error::new(EPERM));
|
||||
}
|
||||
|
||||
// Is it a subprocess of us? In the future, a capability
|
||||
// could bypass this check.
|
||||
match contexts.anchestors(target.ppid).find(|&(id, _context)| id == current.id) {
|
||||
Some((id, context)) => {
|
||||
// Paranoid sanity check, as ptrace security holes
|
||||
// wouldn't be fun
|
||||
assert_eq!(id, current.id);
|
||||
assert_eq!(id, context.read().id);
|
||||
},
|
||||
None => return Err(Error::new(EPERM))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
|
||||
if let Operation::Trace = operation {
|
||||
let mut traced = self.traced.lock();
|
||||
|
||||
if traced.contains(&pid) {
|
||||
if let Operation::Trace { .. } = operation {
|
||||
if !ptrace::try_new_session(pid, id) {
|
||||
// There is no good way to handle id being occupied
|
||||
// for nothing here, is there?
|
||||
return Err(Error::new(EBUSY));
|
||||
}
|
||||
traced.insert(pid);
|
||||
|
||||
let mut target = target.write();
|
||||
target.ptrace_stop = true;
|
||||
}
|
||||
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.write().insert(id, Arc::new(Mutex::new(Handle {
|
||||
flags,
|
||||
pid,
|
||||
@@ -244,7 +274,16 @@ impl Scheme for ProcScheme {
|
||||
|
||||
Ok(len)
|
||||
},
|
||||
Operation::Trace => Err(Error::new(EBADF))
|
||||
Operation::Trace { .. } => {
|
||||
let read = ptrace::recv_events(handle.pid, unsafe {
|
||||
slice::from_raw_parts_mut(
|
||||
buf.as_mut_ptr() as *mut PtraceEvent,
|
||||
buf.len() / mem::size_of::<PtraceEvent>()
|
||||
)
|
||||
}).unwrap_or(0);
|
||||
|
||||
Ok(read * mem::size_of::<PtraceEvent>())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,7 +294,11 @@ impl Scheme for ProcScheme {
|
||||
Arc::clone(handles.get(&id).ok_or(Error::new(EBADF))?)
|
||||
};
|
||||
let mut handle = handle.lock();
|
||||
handle.continue_ignored_child();
|
||||
|
||||
// Some operations borrow Operation:: mutably
|
||||
let pid = handle.pid;
|
||||
let flags = handle.flags;
|
||||
|
||||
let mut first = true;
|
||||
match handle.operation {
|
||||
@@ -320,28 +363,23 @@ impl Scheme for ProcScheme {
|
||||
}
|
||||
};
|
||||
},
|
||||
Operation::Trace => {
|
||||
Operation::Trace { ref mut new_child } => {
|
||||
if buf.len() < 1 {
|
||||
return Ok(0);
|
||||
}
|
||||
let op = buf[0];
|
||||
let sysemu = op & PTRACE_SYSEMU == PTRACE_SYSEMU;
|
||||
|
||||
let mut blocking = handle.flags & O_NONBLOCK != O_NONBLOCK;
|
||||
let mut wait_breakpoint = false;
|
||||
let mut blocking = flags & O_NONBLOCK != O_NONBLOCK;
|
||||
let mut singlestep = false;
|
||||
|
||||
match op & PTRACE_OPERATIONMASK {
|
||||
PTRACE_CONT => { ptrace::cont(handle.pid); },
|
||||
PTRACE_CONT => { ptrace::cont(pid); },
|
||||
PTRACE_SYSCALL | PTRACE_SINGLESTEP => { // <- not a bitwise OR
|
||||
singlestep = op & PTRACE_OPERATIONMASK == PTRACE_SINGLESTEP;
|
||||
ptrace::set_breakpoint(handle.pid, sysemu, singlestep);
|
||||
wait_breakpoint = true;
|
||||
},
|
||||
PTRACE_WAIT => {
|
||||
wait_breakpoint = true;
|
||||
blocking = true;
|
||||
ptrace::set_breakpoint(pid, sysemu, singlestep);
|
||||
},
|
||||
PTRACE_WAIT => blocking = true,
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
}
|
||||
|
||||
@@ -354,7 +392,7 @@ impl Scheme for ProcScheme {
|
||||
first = false;
|
||||
|
||||
let contexts = context::contexts();
|
||||
let context = contexts.get(handle.pid).ok_or(Error::new(ESRCH))?;
|
||||
let context = contexts.get(pid).ok_or(Error::new(ESRCH))?;
|
||||
let mut context = context.write();
|
||||
if let Status::Exited(_) = context.status {
|
||||
return Err(Error::new(ESRCH));
|
||||
@@ -371,8 +409,13 @@ impl Scheme for ProcScheme {
|
||||
break;
|
||||
}
|
||||
|
||||
if wait_breakpoint && blocking {
|
||||
ptrace::wait_breakpoint(handle.pid)?;
|
||||
if blocking {
|
||||
if let Some(event) = ptrace::wait(pid)? {
|
||||
if event.tag == PTRACE_EVENT_CLONE {
|
||||
*new_child = Some(ContextId::from(unsafe { event.data.clone }));
|
||||
}
|
||||
return Ok(0);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(1)
|
||||
@@ -392,6 +435,14 @@ impl Scheme for ProcScheme {
|
||||
}
|
||||
}
|
||||
|
||||
fn fevent(&self, id: usize, _flags: usize) -> Result<usize> {
|
||||
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.pid).expect("proc (fevent): invalid session"))
|
||||
}
|
||||
|
||||
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))?;
|
||||
@@ -401,7 +452,7 @@ impl Scheme for ProcScheme {
|
||||
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());
|
||||
@@ -412,11 +463,11 @@ impl Scheme for ProcScheme {
|
||||
|
||||
fn close(&self, id: usize) -> Result<usize> {
|
||||
let handle = self.handles.write().remove(&id).ok_or(Error::new(EBADF))?;
|
||||
let handle = handle.lock();
|
||||
let mut handle = handle.lock();
|
||||
handle.continue_ignored_child();
|
||||
|
||||
if let Operation::Trace = handle.operation {
|
||||
ptrace::cont(handle.pid);
|
||||
self.traced.lock().remove(&handle.pid);
|
||||
if let Operation::Trace { .. } = handle.operation {
|
||||
ptrace::close_session(handle.pid);
|
||||
}
|
||||
|
||||
let contexts = context::contexts();
|
||||
|
||||
@@ -21,12 +21,12 @@ use crate::paging::{ActivePageTable, InactivePageTable, Page, VirtualAddress, PA
|
||||
use crate::ptrace;
|
||||
use crate::scheme::FileHandle;
|
||||
use crate::start::usermode;
|
||||
use crate::syscall::data::{SigAction, Stat};
|
||||
use crate::syscall::data::{PtraceEvent, PtraceEventContent, SigAction, Stat};
|
||||
use crate::syscall::error::*;
|
||||
use crate::syscall::flag::{CLONE_VFORK, CLONE_VM, CLONE_FS, CLONE_FILES, CLONE_SIGHAND, CLONE_STACK,
|
||||
PROT_EXEC, PROT_READ, PROT_WRITE,
|
||||
SIG_DFL, SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK, SIGCONT, SIGTERM,
|
||||
WCONTINUED, WNOHANG, WUNTRACED, wifcontinued, wifstopped};
|
||||
PROT_EXEC, PROT_READ, PROT_WRITE, PTRACE_EVENT_CLONE,
|
||||
SIG_DFL, SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK, SIGCONT, SIGTERM,
|
||||
WCONTINUED, WNOHANG, WUNTRACED, wifcontinued, wifstopped};
|
||||
use crate::syscall::validate::{validate_slice, validate_slice_mut};
|
||||
use crate::syscall;
|
||||
|
||||
@@ -585,6 +585,22 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<ContextId> {
|
||||
}
|
||||
}
|
||||
|
||||
let ptrace_event = PtraceEvent {
|
||||
tag: PTRACE_EVENT_CLONE,
|
||||
data: PtraceEventContent {
|
||||
clone: pid.into()
|
||||
}
|
||||
};
|
||||
|
||||
if ptrace::send_event(ptrace_event).is_some() {
|
||||
// Freeze the clone, allow ptrace to put breakpoints
|
||||
// to it before it starts
|
||||
let contexts = context::contexts();
|
||||
let context = contexts.get(pid).expect("Newly created context doesn't exist??");
|
||||
let mut context = context.write();
|
||||
context.ptrace_stop = true;
|
||||
}
|
||||
|
||||
// Race to pick up the new process!
|
||||
ipi(IpiKind::Switch, IpiTarget::Other);
|
||||
|
||||
@@ -1109,7 +1125,7 @@ pub fn exit(status: usize) -> ! {
|
||||
};
|
||||
|
||||
// Alert any tracers waiting for process (important: AFTER sending waitpid event)
|
||||
ptrace::close(pid);
|
||||
ptrace::close_tracee(pid);
|
||||
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
|
||||
2
syscall
2
syscall
Submodule syscall updated: 49dd22260b...eddcb80eb7
Reference in New Issue
Block a user