Merge branch 'ptrace-end' into 'master'

Final few ptrace changes

See merge request redox-os/kernel!109
This commit is contained in:
Jeremy Soller
2019-08-21 21:00:13 +00:00
9 changed files with 144 additions and 94 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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)]

View File

@@ -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() };

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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();

Submodule syscall updated: 0e1e7d5d36...6ba71e7e06