Merge branch 'ptrace-end' into 'master'
Final few ptrace changes See merge request redox-os/kernel!109
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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() };
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
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))?;
|
||||
|
||||
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 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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
2
syscall
2
syscall
Submodule syscall updated: 0e1e7d5d36...6ba71e7e06
Reference in New Issue
Block a user