Merge branch 'ptrace-5' into 'master'
Drive ptrace into a wall, prepare for overhaul See merge request redox-os/kernel!106
This commit is contained in:
@@ -1,6 +1,4 @@
|
||||
use crate::{
|
||||
common::unique::Unique,
|
||||
context,
|
||||
interrupt::stack_trace,
|
||||
ptrace,
|
||||
syscall::flag::*
|
||||
@@ -20,15 +18,7 @@ interrupt_stack!(divide_by_zero, stack, {
|
||||
interrupt_stack!(debug, stack, {
|
||||
let mut handled = false;
|
||||
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
if let Some(context) = contexts.current() {
|
||||
let mut context = context.write();
|
||||
if let Some(ref mut kstack) = context.kstack {
|
||||
context.regs = Some((kstack.as_mut_ptr() as usize, Unique::new_unchecked(stack)));
|
||||
}
|
||||
}
|
||||
}
|
||||
let guard = ptrace::set_process_regs(stack);
|
||||
|
||||
// Disable singlestep before their is a breakpoint, since the
|
||||
// breakpoint handler might end up setting it again but unless it
|
||||
@@ -36,20 +26,14 @@ interrupt_stack!(debug, stack, {
|
||||
let had_singlestep = stack.iret.rflags & (1 << 8) == 1 << 8;
|
||||
stack.set_singlestep(false);
|
||||
|
||||
if ptrace::breakpoint_callback(true).is_some() {
|
||||
if ptrace::breakpoint_callback(syscall::PTRACE_SINGLESTEP).is_some() {
|
||||
handled = true;
|
||||
} else {
|
||||
// There was no breakpoint, restore original value
|
||||
stack.set_singlestep(had_singlestep);
|
||||
}
|
||||
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
if let Some(context) = contexts.current() {
|
||||
let mut context = context.write();
|
||||
context.regs = None;
|
||||
}
|
||||
}
|
||||
drop(guard);
|
||||
|
||||
if !handled {
|
||||
println!("Debug trap");
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use crate::common::unique::Unique;
|
||||
use crate::context;
|
||||
use crate::context::timeout;
|
||||
use crate::device::pic;
|
||||
use crate::device::serial::{COM1, COM2};
|
||||
use crate::ipi::{ipi, IpiKind, IpiTarget};
|
||||
use crate::scheme::debug::debug_input;
|
||||
use crate::time;
|
||||
use crate::{context, ptrace, time};
|
||||
|
||||
//resets to 0 in context::switch()
|
||||
pub static PIT_TICKS: AtomicUsize = AtomicUsize::new(0);
|
||||
@@ -62,25 +60,8 @@ interrupt_stack!(pit, stack, {
|
||||
timeout::trigger();
|
||||
|
||||
if PIT_TICKS.fetch_add(1, Ordering::SeqCst) >= 10 {
|
||||
{
|
||||
let contexts = crate::context::contexts();
|
||||
if let Some(context) = contexts.current() {
|
||||
let mut context = context.write();
|
||||
// Make all registers available to e.g. the proc:
|
||||
// scheme
|
||||
if let Some(ref mut kstack) = context.kstack {
|
||||
context.regs = Some((kstack.as_mut_ptr() as usize, Unique::new_unchecked(stack)));
|
||||
}
|
||||
}
|
||||
}
|
||||
let _guard = ptrace::set_process_regs(stack);
|
||||
let _ = context::switch();
|
||||
{
|
||||
let contexts = crate::context::contexts();
|
||||
if let Some(context) = contexts.current() {
|
||||
let mut context = context.write();
|
||||
context.regs = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::arch::macros::InterruptStack;
|
||||
use crate::arch::{gdt, pti};
|
||||
use crate::common::unique::Unique;
|
||||
use crate::{context, ptrace, syscall};
|
||||
use crate::{ptrace, syscall};
|
||||
use x86::shared::msr;
|
||||
|
||||
pub unsafe fn init() {
|
||||
@@ -20,18 +19,10 @@ macro_rules! with_interrupt_stack {
|
||||
(unsafe fn $wrapped:ident($stack:ident) -> usize $code:block) => {
|
||||
#[inline(never)]
|
||||
unsafe fn $wrapped(stack: *mut InterruptStack) {
|
||||
let stack = &mut *stack;
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
if let Some(context) = contexts.current() {
|
||||
let mut context = context.write();
|
||||
if let Some(ref mut kstack) = context.kstack {
|
||||
context.regs = Some((kstack.as_mut_ptr() as usize, Unique::new_unchecked(&mut *stack)));
|
||||
}
|
||||
}
|
||||
}
|
||||
let _guard = ptrace::set_process_regs(stack);
|
||||
|
||||
let is_sysemu = ptrace::breakpoint_callback(false);
|
||||
let is_sysemu = ptrace::breakpoint_callback(syscall::flag::PTRACE_SYSCALL)
|
||||
.map(|fl| fl & syscall::flag::PTRACE_SYSEMU == syscall::flag::PTRACE_SYSEMU);
|
||||
if !is_sysemu.unwrap_or(false) {
|
||||
// If not on a sysemu breakpoint
|
||||
let $stack = &mut *stack;
|
||||
@@ -40,15 +31,7 @@ macro_rules! with_interrupt_stack {
|
||||
if is_sysemu.is_some() {
|
||||
// Only callback if there was a pre-syscall
|
||||
// callback too.
|
||||
ptrace::breakpoint_callback(false);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
if let Some(context) = contexts.current() {
|
||||
let mut context = context.write();
|
||||
context.regs = None;
|
||||
ptrace::breakpoint_callback(::syscall::PTRACE_SYSCALL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,8 +280,8 @@ impl InterruptStack {
|
||||
self.scratch.rdx = all.rdx;
|
||||
self.scratch.rcx = all.rcx;
|
||||
self.scratch.rax = all.rax;
|
||||
// self.iret.rip = all.rip;
|
||||
// self.iret.cs = all.cs;
|
||||
self.iret.rip = all.rip;
|
||||
self.iret.cs = all.cs;
|
||||
// self.iret.rflags = all.eflags;
|
||||
}
|
||||
/// Enables the "Trap Flag" in the FLAGS register, causing the CPU
|
||||
|
||||
@@ -1,3 +1,27 @@
|
||||
#[macro_use]
|
||||
pub mod int_like;
|
||||
pub mod unique;
|
||||
|
||||
/// Debug macro, lifted from the std
|
||||
#[macro_export]
|
||||
macro_rules! dbg {
|
||||
() => {
|
||||
$crate::println!("[{}:{}]", file!(), line!());
|
||||
};
|
||||
($val:expr) => {
|
||||
// Use of `match` here is intentional because it affects the lifetimes
|
||||
// of temporaries - https://stackoverflow.com/a/48732525/1063961
|
||||
match $val {
|
||||
tmp => {
|
||||
$crate::println!("[{}:{}] {} = {:#?}",
|
||||
file!(), line!(), stringify!($val), &tmp);
|
||||
tmp
|
||||
}
|
||||
}
|
||||
};
|
||||
// Trailing comma with single argument is ignored
|
||||
($val:expr,) => { $crate::dbg!($val) };
|
||||
($($val:expr),+ $(,)?) => {
|
||||
($($crate::dbg!($val)),+,)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ unsafe impl<T> Sync for Unique<T> {}
|
||||
|
||||
impl<T> Unique<T> {
|
||||
pub fn new(ptr: *mut T) -> Self {
|
||||
Self(NonNull::new(ptr).unwrap())
|
||||
Self(NonNull::new(ptr).expect("Did not expect pointer to be null"))
|
||||
}
|
||||
pub unsafe fn new_unchecked(ptr: *mut T) -> Self {
|
||||
Self(NonNull::new_unchecked(ptr))
|
||||
|
||||
@@ -142,8 +142,8 @@ pub struct Context {
|
||||
pub kfx: Option<Box<[u8]>>,
|
||||
/// Kernel stack
|
||||
pub kstack: Option<Box<[u8]>>,
|
||||
/// Kernel signal backup
|
||||
pub ksig: Option<(arch::Context, Option<Box<[u8]>>, Option<Box<[u8]>>)>,
|
||||
/// Kernel signal backup: Registers, Kernel FX, Kernel Stack, Signal number
|
||||
pub ksig: Option<(arch::Context, Option<Box<[u8]>>, Option<Box<[u8]>>, u8)>,
|
||||
/// Restore ksig context on next switch
|
||||
pub ksig_restore: bool,
|
||||
/// Executable image
|
||||
|
||||
@@ -2,7 +2,7 @@ use alloc::sync::Arc;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::collections::BTreeMap;
|
||||
use core::alloc::{GlobalAlloc, Layout};
|
||||
use core::mem;
|
||||
use core::{iter, mem};
|
||||
use core::sync::atomic::Ordering;
|
||||
use crate::paging;
|
||||
use spin::RwLock;
|
||||
@@ -30,6 +30,15 @@ impl ContextList {
|
||||
self.map.get(&id)
|
||||
}
|
||||
|
||||
/// Get an iterator of all parents
|
||||
pub fn anchestors(&'_ self, id: ContextId) -> impl Iterator<Item = (ContextId, &Arc<RwLock<Context>>)> + '_ {
|
||||
iter::successors(self.get(id).map(|context| (id, context)), move |(_id, context)| {
|
||||
let context = context.read();
|
||||
let id = context.ppid;
|
||||
self.get(id).map(|context| (id, context))
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the current context.
|
||||
pub fn current(&self) -> Option<&Arc<RwLock<Context>>> {
|
||||
self.map.get(&super::CONTEXT_ID.load(Ordering::SeqCst))
|
||||
|
||||
@@ -3,8 +3,14 @@ use core::mem;
|
||||
|
||||
use crate::context::{contexts, switch, Status, WaitpidKey};
|
||||
use crate::start::usermode;
|
||||
use crate::syscall;
|
||||
use crate::syscall::flag::{SIG_DFL, SIG_IGN, SIGCHLD, SIGCONT, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU};
|
||||
use crate::{ptrace, syscall};
|
||||
use crate::syscall::flag::{PTRACE_EVENT_SIGNAL, PTRACE_SIGNAL, SIG_DFL, SIG_IGN, SIGCHLD, SIGCONT, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU};
|
||||
use crate::syscall::data::{PtraceEvent, PtraceEventData};
|
||||
|
||||
pub fn is_user_handled(handler: Option<extern "C" fn(usize)>) -> bool {
|
||||
let handler = handler.map(|ptr| ptr as usize).unwrap_or(0);
|
||||
handler != SIG_DFL && handler != SIG_IGN
|
||||
}
|
||||
|
||||
pub extern "C" fn signal_handler(sig: usize) {
|
||||
let (action, restorer) = {
|
||||
@@ -15,7 +21,12 @@ pub extern "C" fn signal_handler(sig: usize) {
|
||||
actions[sig]
|
||||
};
|
||||
|
||||
let handler = action.sa_handler as usize;
|
||||
ptrace::send_event(PtraceEvent {
|
||||
tag: PTRACE_EVENT_SIGNAL,
|
||||
data: PtraceEventData { signal: sig }
|
||||
});
|
||||
|
||||
let handler = action.sa_handler.map(|ptr| ptr as usize).unwrap_or(0);
|
||||
if handler == SIG_DFL {
|
||||
match sig {
|
||||
SIGCHLD => {
|
||||
@@ -89,6 +100,8 @@ pub extern "C" fn signal_handler(sig: usize) {
|
||||
} else {
|
||||
// println!("Call {:X}", handler);
|
||||
|
||||
ptrace::breakpoint_callback(PTRACE_SIGNAL);
|
||||
|
||||
unsafe {
|
||||
let mut sp = crate::USER_SIGSTACK_OFFSET + crate::USER_SIGSTACK_SIZE - 256;
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ pub unsafe fn switch() -> bool {
|
||||
let arch = (&mut *to_ptr).arch.clone();
|
||||
let kfx = (&mut *to_ptr).kfx.clone();
|
||||
let kstack = (&mut *to_ptr).kstack.clone();
|
||||
(&mut *to_ptr).ksig = Some((arch, kfx, kstack));
|
||||
(&mut *to_ptr).ksig = Some((arch, kfx, kstack, sig));
|
||||
(&mut *to_ptr).arch.signal_stack(signal_handler, sig);
|
||||
}
|
||||
|
||||
|
||||
344
src/ptrace.rs
344
src/ptrace.rs
@@ -9,18 +9,151 @@ use crate::{
|
||||
}
|
||||
},
|
||||
common::unique::Unique,
|
||||
context::{self, Context, ContextId, Status},
|
||||
context::{self, signal, 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::*
|
||||
};
|
||||
|
||||
// ____ _
|
||||
// / ___| ___ ___ ___(_) ___ _ __ ___
|
||||
// \___ \ / _ \/ __/ __| |/ _ \| '_ \/ __|
|
||||
// ___) | __/\__ \__ \ | (_) | | | \__ \
|
||||
// |____/ \___||___/___/_|\___/|_| |_|___/
|
||||
|
||||
#[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
|
||||
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,23 @@ use syscall::error::*;
|
||||
// |____/|_| \___|\__,_|_|\_\ .__/ \___/|_|_| |_|\__|___/
|
||||
// |_|
|
||||
|
||||
struct Handle {
|
||||
#[derive(Debug)]
|
||||
struct Breakpoint {
|
||||
tracee: Arc<WaitCondition>,
|
||||
tracer: Arc<WaitCondition>,
|
||||
reached: bool,
|
||||
|
||||
sysemu: bool,
|
||||
singlestep: bool
|
||||
flags: u8
|
||||
}
|
||||
|
||||
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,92 +186,108 @@ pub fn cont(pid: ContextId) {
|
||||
inner_cont(pid);
|
||||
}
|
||||
|
||||
/// Create a new breakpoint for the specified tracee, optionally with a sysemu flag
|
||||
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())
|
||||
)
|
||||
};
|
||||
/// 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: u8) {
|
||||
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
|
||||
flags
|
||||
});
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// Note: Don't call while holding any locks, this will switch contexts
|
||||
pub fn breakpoint_callback(singlestep: bool) -> Option<bool> {
|
||||
pub fn breakpoint_callback(match_flags: u8) -> Option<u8> {
|
||||
// Can't hold any locks when executing wait()
|
||||
let (tracee, sysemu) = {
|
||||
let (tracee, flags) = {
|
||||
let contexts = context::contexts();
|
||||
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?
|
||||
|
||||
// if singlestep && !breakpoint.singlestep {
|
||||
if breakpoint.singlestep != singlestep {
|
||||
if breakpoint.flags & PTRACE_OPERATIONMASK != match_flags & PTRACE_OPERATIONMASK {
|
||||
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
|
||||
breakpoint.flags
|
||||
)
|
||||
};
|
||||
|
||||
while !tracee.wait() {}
|
||||
|
||||
Some(sysemu)
|
||||
Some(flags)
|
||||
}
|
||||
|
||||
/// 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(())
|
||||
}
|
||||
|
||||
// ____ _ _
|
||||
@@ -158,6 +297,43 @@ pub fn close(pid: ContextId) {
|
||||
// |_| \_\___|\__, |_|___/\__\___|_| |___/
|
||||
// |___/
|
||||
|
||||
pub struct ProcessRegsGuard;
|
||||
|
||||
/// Make all registers available to e.g. the proc: scheme
|
||||
/// ---
|
||||
/// For use inside arch-specific code to assign the pointer of the
|
||||
/// interupt stack to the current process. Meant to reduce the amount
|
||||
/// of ptrace-related code that has to lie in arch-specific bits.
|
||||
/// ```rust,ignore
|
||||
/// let _guard = ptrace::set_process_regs(pointer);
|
||||
/// ...
|
||||
/// // (_guard implicitly dropped)
|
||||
/// ```
|
||||
pub fn set_process_regs(pointer: *mut InterruptStack) -> Option<ProcessRegsGuard> {
|
||||
let contexts = context::contexts();
|
||||
let context = contexts.current()?;
|
||||
let mut context = context.write();
|
||||
|
||||
let kstack = context.kstack.as_mut()?;
|
||||
|
||||
context.regs = Some((kstack.as_mut_ptr() as usize, Unique::new(pointer)));
|
||||
Some(ProcessRegsGuard)
|
||||
}
|
||||
|
||||
impl Drop for ProcessRegsGuard {
|
||||
fn drop(&mut self) {
|
||||
fn clear_process_regs() -> Option<()> {
|
||||
let contexts = context::contexts();
|
||||
let context = contexts.current()?;
|
||||
let mut context = context.write();
|
||||
|
||||
context.regs = None;
|
||||
Some(())
|
||||
}
|
||||
clear_process_regs();
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the InterruptStack pointer, but relative to the specified
|
||||
/// stack instead of the original.
|
||||
pub unsafe fn rebase_regs_ptr(
|
||||
@@ -185,18 +361,44 @@ pub unsafe fn rebase_regs_ptr_mut(
|
||||
/// restored and otherwise undo all your changes. See `update(...)` in
|
||||
/// context/switch.rs.
|
||||
pub unsafe fn regs_for(context: &Context) -> Option<&InterruptStack> {
|
||||
Some(&*match context.ksig {
|
||||
Some((_, _, ref kstack)) => rebase_regs_ptr(context.regs, kstack.as_ref())?,
|
||||
None => context.regs?.1.as_ptr()
|
||||
})
|
||||
let signal_backup_regs = match context.ksig {
|
||||
None => None,
|
||||
Some((_, _, ref kstack, signum)) => {
|
||||
let is_user_handled = {
|
||||
let actions = context.actions.lock();
|
||||
signal::is_user_handled(actions[signum as usize].0.sa_handler)
|
||||
};
|
||||
if is_user_handled {
|
||||
None
|
||||
} else {
|
||||
Some(rebase_regs_ptr(context.regs, kstack.as_ref())?)
|
||||
}
|
||||
}
|
||||
};
|
||||
signal_backup_regs
|
||||
.or_else(|| context.regs.map(|regs| regs.1.as_ptr() as *const _))
|
||||
.map(|ptr| &*ptr)
|
||||
}
|
||||
|
||||
/// Mutable version of `regs_for`
|
||||
pub unsafe fn regs_for_mut(context: &mut Context) -> Option<&mut InterruptStack> {
|
||||
Some(&mut *match context.ksig {
|
||||
Some((_, _, ref mut kstack)) => rebase_regs_ptr_mut(context.regs, kstack.as_mut())?,
|
||||
None => context.regs?.1.as_ptr()
|
||||
})
|
||||
let signal_backup_regs = match context.ksig {
|
||||
None => None,
|
||||
Some((_, _, ref mut kstack, signum)) => {
|
||||
let is_user_handled = {
|
||||
let actions = context.actions.lock();
|
||||
signal::is_user_handled(actions[signum as usize].0.sa_handler)
|
||||
};
|
||||
if is_user_handled {
|
||||
None
|
||||
} else {
|
||||
Some(rebase_regs_ptr_mut(context.regs, kstack.as_mut())?)
|
||||
}
|
||||
}
|
||||
};
|
||||
signal_backup_regs
|
||||
.or_else(|| context.regs.map(|regs| regs.1.as_ptr()))
|
||||
.map(|ptr| &mut *ptr)
|
||||
}
|
||||
|
||||
// __ __
|
||||
|
||||
@@ -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,36 +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 context = contexts.get(pid).ok_or(Error::new(ESRCH))?;
|
||||
let target = contexts.get(pid).ok_or(Error::new(ESRCH))?;
|
||||
|
||||
{
|
||||
// TODO: Put better security here?
|
||||
let target = target.read();
|
||||
|
||||
let context = context.read();
|
||||
if uid != 0 && gid != 0
|
||||
&& uid != context.euid && gid != context.egid {
|
||||
return Err(Error::new(EPERM));
|
||||
if let Status::Exited(_) = target.status {
|
||||
return Err(Error::new(ESRCH));
|
||||
}
|
||||
}
|
||||
|
||||
if let Operation::Trace = operation {
|
||||
let mut traced = self.traced.lock();
|
||||
// Unless root, check security
|
||||
if uid != 0 && gid != 0 {
|
||||
let current = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let current = current.read();
|
||||
|
||||
if traced.contains(&pid) {
|
||||
return Err(Error::new(EBUSY));
|
||||
// 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))
|
||||
}
|
||||
}
|
||||
traced.insert(pid);
|
||||
|
||||
let mut context = context.write();
|
||||
context.ptrace_stop = true;
|
||||
}
|
||||
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
let mut target = target.write();
|
||||
target.ptrace_stop = true;
|
||||
}
|
||||
|
||||
self.handles.write().insert(id, Arc::new(Mutex::new(Handle {
|
||||
flags,
|
||||
pid,
|
||||
@@ -230,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>())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,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 {
|
||||
@@ -306,28 +363,22 @@ 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_SYSCALL | PTRACE_SINGLESTEP => { // <- not a bitwise OR
|
||||
PTRACE_CONT => { ptrace::cont(pid); },
|
||||
PTRACE_SYSCALL | PTRACE_SINGLESTEP | PTRACE_SIGNAL => { // <- 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, op);
|
||||
},
|
||||
PTRACE_WAIT => blocking = true,
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
}
|
||||
|
||||
@@ -340,7 +391,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));
|
||||
@@ -357,8 +408,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)
|
||||
@@ -378,6 +434,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))?;
|
||||
@@ -387,7 +451,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());
|
||||
@@ -398,11 +462,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();
|
||||
|
||||
@@ -81,6 +81,17 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -
|
||||
},
|
||||
d
|
||||
),
|
||||
SYS_FCHMOD => format!(
|
||||
"fchmod({}, {:#o})",
|
||||
b,
|
||||
c
|
||||
),
|
||||
SYS_FCHOWN => format!(
|
||||
"fchown({}, {}, {})",
|
||||
b,
|
||||
c,
|
||||
d
|
||||
),
|
||||
SYS_FCNTL => format!(
|
||||
"fcntl({}, {} ({}), {:#X})",
|
||||
b,
|
||||
@@ -113,6 +124,11 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -
|
||||
c,
|
||||
d
|
||||
),
|
||||
SYS_FRENAME => format!(
|
||||
"frename({}, {:?})",
|
||||
b,
|
||||
validate_slice(c as *const u8, d).map(ByteStr),
|
||||
),
|
||||
SYS_FSTAT => format!(
|
||||
"fstat({}, {:?})",
|
||||
b,
|
||||
@@ -136,6 +152,14 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -
|
||||
b,
|
||||
c
|
||||
),
|
||||
SYS_FUTIMENS => format!(
|
||||
"futimens({}, {:?})",
|
||||
b,
|
||||
validate_slice(
|
||||
c as *const TimeSpec,
|
||||
d/mem::size_of::<TimeSpec>()
|
||||
),
|
||||
),
|
||||
|
||||
SYS_BRK => format!(
|
||||
"brk({:#X})",
|
||||
@@ -200,7 +224,9 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -
|
||||
SYS_GETEUID => format!("geteuid()"),
|
||||
SYS_GETGID => format!("getgid()"),
|
||||
SYS_GETNS => format!("getns()"),
|
||||
SYS_GETPGID => format!("getpgid()"),
|
||||
SYS_GETPID => format!("getpid()"),
|
||||
SYS_GETPPID => format!("getppid()"),
|
||||
SYS_GETUID => format!("getuid()"),
|
||||
SYS_IOPL => format!(
|
||||
"iopl({})",
|
||||
|
||||
@@ -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, PtraceEventData, 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: PtraceEventData {
|
||||
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);
|
||||
|
||||
@@ -1068,8 +1084,6 @@ pub fn exit(status: usize) -> ! {
|
||||
context.id
|
||||
};
|
||||
|
||||
ptrace::close(pid);
|
||||
|
||||
// Files must be closed while context is valid so that messages can be passed
|
||||
for (_fd, file_option) in close_files.drain(..).enumerate() {
|
||||
if let Some(file) = file_option {
|
||||
@@ -1136,6 +1150,9 @@ pub fn exit(status: usize) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
// Alert any tracers waiting for process (important: AFTER sending waitpid event)
|
||||
ptrace::close_tracee(pid);
|
||||
|
||||
if pid == ContextId::from(1) {
|
||||
println!("Main kernel thread exited with status {:X}", status);
|
||||
|
||||
|
||||
2
syscall
2
syscall
Submodule syscall updated: 49dd22260b...844650c4fb
Reference in New Issue
Block a user