Verified Commit ce85971c authored by jD91mZM2's avatar jD91mZM2
Browse files

WIP(ptrace): Bare-bones tracing functionality

parent ccaaf08e
use crate::arch::macros::InterruptStack;
use crate::arch::{gdt, pti};
use crate::common::unique::Unique;
use crate::{context, syscall};
use crate::{context, ptrace, syscall};
use x86::shared::msr;
pub unsafe fn init() {
......@@ -33,17 +33,19 @@ macro_rules! with_interrupt_stack {
}
}
let ret = {
if !ptrace::syscall_callback().unwrap_or(false) {
// If not on a sysemu breakpoint
let $stack = &mut *stack;
$code
};
$stack.interrupt_stack.scratch.rax = $code;
ptrace::syscall_callback();
}
{
let contexts = context::contexts();
if let Some(context) = contexts.current() {
let mut context = context.write();
context.interrupt_stack = None;
stack.interrupt_stack.scratch.rax = ret;
}
}
}
......
......@@ -168,6 +168,10 @@ pub struct Context {
pub actions: Arc<Mutex<Vec<(SigAction, usize)>>>,
/// The interrupt stack which holds all the context's registers
pub interrupt_stack: Option<Unique<InterruptStack>>,
/// A somewhat hacky way to initially stop a context when creating
/// a new instance of the proc: scheme, entirely separate from any
/// signals or any other way to restart a process.
pub ptrace_stop: bool
}
impl Context {
......@@ -219,7 +223,8 @@ impl Context {
},
0
); 128])),
interrupt_stack: None
interrupt_stack: None,
ptrace_stop: false
}
}
......
......@@ -55,7 +55,7 @@ unsafe fn update(context: &mut Context, cpu_id: usize) {
unsafe fn runnable(context: &Context, cpu_id: usize) -> bool {
// Switch to context if it needs to run, is not currently running, and is owned by the current CPU
!context.running && context.status == Status::Runnable && context.cpu_id == Some(cpu_id)
!context.running && !context.ptrace_stop && context.status == Status::Runnable && context.cpu_id == Some(cpu_id)
}
/// Switch to the next context
......
......@@ -8,10 +8,10 @@ use alloc::{
sync::Arc
};
use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
use syscall::data::IntRegisters;
struct Handle {
condition: Arc<WaitCondition>,
tracee: Arc<WaitCondition>,
tracer: Arc<WaitCondition>,
sysemu: bool
}
......@@ -27,44 +27,69 @@ fn breakpoints_mut() -> RwLockWriteGuard<'static, BTreeMap<ContextId, Handle>> {
SYSCALL_BREAKPOINTS.call_once(init_breakpoints).write()
}
pub fn ptrace_cont(pid: ContextId) {
pub fn cont(pid: ContextId) {
// println!("Continuing {:?}", pid);
let breakpoints = breakpoints();
if let Some(breakpoint) = breakpoints.get(&pid) {
breakpoint.condition.notify();
breakpoint.tracee.notify();
}
}
pub fn ptrace_break_syscall(pid: ContextId, sysemu: bool) {
pub fn break_syscall(pid: ContextId, sysemu: bool) {
// Continue execution of the tracee and therefore also release
// locks on breakpoints(). This has to be done before trying a
// mutable lock.
ptrace_cont(pid);
cont(pid);
let (tracee, tracer) = match breakpoints_mut().remove(&pid) {
Some(breakpoint) => (breakpoint.tracee, breakpoint.tracer),
None => (
Arc::new(WaitCondition::new()),
Arc::new(WaitCondition::new())
)
};
// TODO: reuse WaitConditions?
// println!("Breaking {:?} (sysemu: {:?})", pid, sysemu);
breakpoints_mut().insert(pid, Handle {
condition: Arc::new(WaitCondition::new()),
tracee,
tracer,
sysemu
});
}
pub fn wait_breakpoint(pid: ContextId) {
let tracer = {
let breakpoints = breakpoints();
match breakpoints.get(&pid) {
Some(breakpoint) => Arc::clone(&breakpoint.tracer),
None => return
}
};
// println!("Waiting for breakpoint on {:?}", pid);
while !tracer.wait() {}
}
/// Note: Don't call while holding any locks, this will switch contexts
pub fn ptrace_syscall_callback() -> Option<bool> {
pub fn syscall_callback() -> Option<bool> {
// Can't hold any locks when executing wait()
let (condition, sysemu) = {
let (tracee, sysemu) = {
let contexts = context::contexts();
let context = contexts.current()?;
let context = context.read();
let breakpoints = breakpoints();
let breakpoint = breakpoints.get(&context.id)?;
// println!("{:?} reached breakpoint (sysemu: {:?})", context.id, breakpoint.sysemu);
breakpoint.tracer.notify();
(
Arc::clone(&breakpoint.condition),
Arc::clone(&breakpoint.tracee),
breakpoint.sysemu
)
};
// TODO: How should signals affect the wait?
while !condition.wait() {}
while !tracee.wait() {}
Some(sysemu)
}
use crate::{
context::{self, ContextId},
syscall
ptrace
};
use alloc::collections::BTreeMap;
use alloc::collections::{BTreeMap, BTreeSet};
use core::{
cmp,
mem,
slice,
sync::atomic::{AtomicUsize, Ordering}
};
use spin::RwLock;
use spin::{Mutex, RwLock};
use ::syscall::{
data::{IntRegisters, FloatRegisters},
error::*,
......@@ -32,26 +32,29 @@ enum Operation {
#[derive(Clone, Copy)]
struct Handle {
flags: usize,
pid: ContextId,
operation: Operation
}
pub struct ProcScheme {
next_id: AtomicUsize,
handles: RwLock<BTreeMap<usize, Handle>>
handles: RwLock<BTreeMap<usize, Handle>>,
traced: Mutex<BTreeSet<ContextId>>
}
impl ProcScheme {
pub fn new() -> Self {
Self {
next_id: AtomicUsize::new(0),
handles: RwLock::new(BTreeMap::new())
handles: RwLock::new(BTreeMap::new()),
traced: Mutex::new(BTreeSet::new())
}
}
}
impl Scheme for ProcScheme {
fn open(&self, path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
fn open(&self, path: &[u8], flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
let path = core::str::from_utf8(path).map_err(|_| Error::new(EINVAL))?;
let mut parts = path.splitn(2, '/');
let pid = parts.next()
......@@ -66,14 +69,28 @@ impl Scheme for ProcScheme {
_ => return Err(Error::new(EINVAL))
};
// TODO: Put security here!!! Maybe check if user/group owns the process?
let contexts = context::contexts();
let context = contexts.get(pid).ok_or(Error::new(ESRCH))?;
{
// TODO: Put security here!!! Maybe check if user/group owns the process?
}
if let Operation::Trace = operation {
syscall::kill(pid, SIGSTOP)?;
let mut traced = self.traced.lock();
if traced.contains(&pid) {
return Err(Error::new(EBUSY));
}
traced.insert(pid);
let mut context = context.write();
context.ptrace_stop = true;
}
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
self.handles.write().insert(id, Handle {
flags,
pid,
operation
});
......@@ -88,10 +105,12 @@ impl Scheme for ProcScheme {
/// let regs = syscall::dup(trace, "regs/int")?;
/// ```
fn dup(&self, old_id: usize, buf: &[u8]) -> Result<usize> {
let handles = self.handles.read();
let handle = handles.get(&old_id).ok_or(Error::new(EBADF))?;
let mut path = {
let handles = self.handles.read();
let handle = handles.get(&old_id).ok_or(Error::new(EBADF))?;
let mut path = format!("proc:{}/", handle.pid.into()).into_bytes();
format!("{}/", handle.pid.into()).into_bytes()
};
path.extend_from_slice(buf);
// NOTE: If security relies on uid or gid, DO NOT ZERO THEM (0=root)
......@@ -101,16 +120,15 @@ impl Scheme for ProcScheme {
fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
// Can't hold locks during the context switch later when
// waiting for a process to stop running.
let (operation, pid) = {
let handle = {
let handles = self.handles.read();
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
(handle.operation, handle.pid)
*handles.get(&id).ok_or(Error::new(EBADF))?
};
match operation {
match handle.operation {
Operation::Memory => {
let contexts = context::contexts();
let context = contexts.get(pid).ok_or(Error::new(ESRCH))?;
let context = contexts.get(handle.pid).ok_or(Error::new(ESRCH))?;
let context = context.read();
for grant in &*context.grants.lock() {
......@@ -132,7 +150,7 @@ impl Scheme for ProcScheme {
first = false;
let contexts = context::contexts();
let context = contexts.get(pid).ok_or(Error::new(ESRCH))?;
let context = contexts.get(handle.pid).ok_or(Error::new(ESRCH))?;
let context = context.read();
break match kind {
......@@ -142,7 +160,6 @@ impl Scheme for ProcScheme {
},
RegsKind::Int => match context.interrupt_stack {
None => {
println!("No interrupt_stack");
// Another CPU is running this process, wait until it's stopped.
continue;
},
......@@ -174,14 +191,13 @@ impl Scheme for ProcScheme {
fn write(&self, id: usize, buf: &[u8]) -> Result<usize> {
// Can't hold locks during the context switch later when
// waiting for a process to stop running.
let (operation, pid) = {
let handle = {
let handles = self.handles.read();
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
(handle.operation, handle.pid)
*handles.get(&id).ok_or(Error::new(EBADF))?
};
let mut first = true;
match operation {
match handle.operation {
Operation::Memory => unimplemented!(),
Operation::Regs(kind) => loop {
if !first {
......@@ -190,8 +206,8 @@ impl Scheme for ProcScheme {
first = false;
let contexts = context::contexts();
let context = contexts.get(pid).ok_or(Error::new(ESRCH))?;
let mut context = context.write();
let context = contexts.get(handle.pid).ok_or(Error::new(ESRCH))?;
let context = context.write();
break match kind {
RegsKind::Float => {
......@@ -219,7 +235,39 @@ impl Scheme for ProcScheme {
}
};
},
Operation::Trace => Err(Error::new(EBADF))
Operation::Trace => {
if buf.len() < 1 {
return Ok(0);
}
let op = buf[0];
let sysemu = op & PTRACE_SYSEMU == PTRACE_SYSEMU;
let mut wait_breakpoint = false;
match op & PTRACE_OPERATIONMASK {
PTRACE_CONT => { ptrace::cont(handle.pid); },
PTRACE_SINGLESTEP => unimplemented!(),
PTRACE_SYSCALL => {
ptrace::break_syscall(handle.pid, sysemu);
wait_breakpoint = true;
},
_ => return Err(Error::new(EINVAL))
}
{
let contexts = context::contexts();
let context = contexts.get(handle.pid).ok_or(Error::new(ESRCH))?;
let mut context = context.write();
context.ptrace_stop = false;
}
if wait_breakpoint && handle.flags & O_NONBLOCK != O_NONBLOCK {
ptrace::wait_breakpoint(handle.pid);
}
Ok(1)
}
}
}
......@@ -241,7 +289,14 @@ impl Scheme for ProcScheme {
}
fn close(&self, id: usize) -> Result<usize> {
self.handles.write().remove(&id);
let handle = self.handles.write().remove(&id).ok_or(Error::new(EBADF))?;
ptrace::cont(handle.pid);
let contexts = context::contexts();
if let Some(context) = contexts.get(handle.pid) {
let mut context = context.write();
context.ptrace_stop = false;
}
Ok(0)
}
}
......@@ -160,21 +160,14 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, bp: u
}
}
/*
let debug = {
let contexts = crate::context::contexts();
if let Some(context_lock) = contexts.current() {
let context = context_lock.read();
let name_raw = context.name.lock();
let name = unsafe { core::str::from_utf8_unchecked(&name_raw) };
if name == "initfs:bin/init" {
if a == SYS_CLOCK_GETTIME {
false
} else if (a == SYS_WRITE || a == SYS_FSYNC) && (b == 1 || b == 2) {
false
} else {
true
}
if name == "file:/bin/regs" {
true
} else {
false
}
......@@ -192,7 +185,6 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, bp: u
println!("{}", debug::format_call(a, b, c, d, e, f));
}
*/
// The next lines set the current syscall in the context struct, then once the inner() function
// completes, we set the current syscall to none.
......@@ -217,7 +209,6 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, bp: u
}
}
/*
if debug {
let contexts = crate::context::contexts();
if let Some(context_lock) = contexts.current() {
......@@ -236,7 +227,6 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, bp: u
}
}
}
*/
// errormux turns Result<usize> into -errno
Error::mux(result)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment