First step for ptrace overhaul
This commit is contained in:
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -55,7 +55,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.0.4"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@@ -175,7 +175,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
name = "kernel"
|
||||
version = "0.1.54"
|
||||
dependencies = [
|
||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clippy 0.0.209 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"goblin 0.0.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"linked_list_allocator 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -268,7 +268,7 @@ name = "raw-cpuid"
|
||||
version = "4.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cc 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -495,7 +495,7 @@ dependencies = [
|
||||
"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6"
|
||||
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
||||
"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
|
||||
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
|
||||
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
|
||||
"checksum cargo_metadata 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1efca0b863ca03ed4c109fb1c55e0bc4bbeb221d3e103d86251046b06a526bd0"
|
||||
"checksum cc 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5f3fee5eeb60324c2781f1e41286bdee933850fff9b3c672587fed5ec58c83"
|
||||
"checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4"
|
||||
|
||||
@@ -26,7 +26,7 @@ interrupt_stack!(debug, stack, {
|
||||
let had_singlestep = stack.iret.rflags & (1 << 8) == 1 << 8;
|
||||
stack.set_singlestep(false);
|
||||
|
||||
if ptrace::breakpoint_callback(syscall::PTRACE_SINGLESTEP).is_some() {
|
||||
if ptrace::breakpoint_callback(PTRACE_STOP_SINGLESTEP, None).is_some() {
|
||||
handled = true;
|
||||
} else {
|
||||
// There was no breakpoint, restore original value
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::arch::macros::InterruptStack;
|
||||
use crate::arch::{gdt, pti};
|
||||
use crate::syscall::flag::{PTRACE_STOP_PRE_SYSCALL, PTRACE_STOP_POST_SYSCALL, PTRACE_FLAG_SYSEMU};
|
||||
use crate::{ptrace, syscall};
|
||||
use x86::shared::msr;
|
||||
|
||||
@@ -21,18 +22,14 @@ macro_rules! with_interrupt_stack {
|
||||
unsafe fn $wrapped(stack: *mut InterruptStack) {
|
||||
let _guard = ptrace::set_process_regs(stack);
|
||||
|
||||
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) {
|
||||
ptrace::breakpoint_callback(PTRACE_STOP_PRE_SYSCALL, None);
|
||||
let not_sysemu = ptrace::next_breakpoint().map(|b| b & PTRACE_FLAG_SYSEMU != PTRACE_FLAG_SYSEMU);
|
||||
if not_sysemu.unwrap_or(true) {
|
||||
// If not on a sysemu breakpoint
|
||||
let $stack = &mut *stack;
|
||||
$stack.scratch.rax = $code;
|
||||
|
||||
if is_sysemu.is_some() {
|
||||
// Only callback if there was a pre-syscall
|
||||
// callback too.
|
||||
ptrace::breakpoint_callback(::syscall::PTRACE_SYSCALL);
|
||||
}
|
||||
ptrace::breakpoint_callback(PTRACE_STOP_POST_SYSCALL, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use alloc::sync::Arc;
|
||||
use core::mem;
|
||||
use syscall::data::PtraceEvent;
|
||||
use syscall::flag::{PTRACE_STOP_SIGNAL, SIG_DFL, SIG_IGN, SIGCHLD, SIGCONT, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU};
|
||||
use syscall::ptrace_event;
|
||||
|
||||
use crate::context::{contexts, switch, Status, WaitpidKey};
|
||||
use crate::start::usermode;
|
||||
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};
|
||||
use crate::ptrace;
|
||||
|
||||
pub fn is_user_handled(handler: Option<extern "C" fn(usize)>) -> bool {
|
||||
let handler = handler.map(|ptr| ptr as usize).unwrap_or(0);
|
||||
@@ -21,12 +22,10 @@ pub extern "C" fn signal_handler(sig: usize) {
|
||||
actions[sig]
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
ptrace::breakpoint_callback(PTRACE_STOP_SIGNAL, Some(ptrace_event!(PTRACE_STOP_SIGNAL, sig, handler)));
|
||||
|
||||
if handler == SIG_DFL {
|
||||
match sig {
|
||||
SIGCHLD => {
|
||||
@@ -92,7 +91,7 @@ pub extern "C" fn signal_handler(sig: usize) {
|
||||
},
|
||||
_ => {
|
||||
// println!("Exit {}", sig);
|
||||
syscall::exit(sig);
|
||||
crate::syscall::exit(sig);
|
||||
}
|
||||
}
|
||||
} else if handler == SIG_IGN {
|
||||
@@ -100,8 +99,6 @@ 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;
|
||||
|
||||
@@ -114,5 +111,5 @@ pub extern "C" fn signal_handler(sig: usize) {
|
||||
}
|
||||
}
|
||||
|
||||
syscall::sigreturn().unwrap();
|
||||
crate::syscall::sigreturn().unwrap();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
//! The backend of the "proc:" scheme. Most internal breakpoint
|
||||
//! handling should go here, unless they closely depend on the design
|
||||
//! of the scheme.
|
||||
|
||||
use crate::{
|
||||
arch::{
|
||||
macros::InterruptStack,
|
||||
@@ -33,7 +37,8 @@ use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
use syscall::{
|
||||
data::PtraceEvent,
|
||||
error::*,
|
||||
flag::*
|
||||
flag::*,
|
||||
ptrace_event
|
||||
};
|
||||
|
||||
// ____ _
|
||||
@@ -44,10 +49,11 @@ use syscall::{
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Session {
|
||||
file_id: usize,
|
||||
events: VecDeque<PtraceEvent>,
|
||||
breakpoint: Option<Breakpoint>,
|
||||
tracer: Arc<WaitCondition>
|
||||
events: VecDeque<PtraceEvent>,
|
||||
file_id: usize,
|
||||
tracee: Arc<WaitCondition>,
|
||||
tracer: Arc<WaitCondition>,
|
||||
}
|
||||
|
||||
type SessionMap = BTreeMap<ContextId, Session>;
|
||||
@@ -73,10 +79,11 @@ pub fn try_new_session(pid: ContextId, file_id: usize) -> bool {
|
||||
Entry::Occupied(_) => false,
|
||||
Entry::Vacant(vacant) => {
|
||||
vacant.insert(Session {
|
||||
file_id,
|
||||
events: VecDeque::new(),
|
||||
breakpoint: None,
|
||||
tracer: Arc::new(WaitCondition::new())
|
||||
events: VecDeque::new(),
|
||||
file_id,
|
||||
tracee: Arc::new(WaitCondition::new()),
|
||||
tracer: Arc::new(WaitCondition::new()),
|
||||
});
|
||||
true
|
||||
}
|
||||
@@ -107,9 +114,7 @@ pub fn session_fevent_flags(pid: ContextId) -> Option<usize> {
|
||||
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();
|
||||
}
|
||||
session.tracee.notify();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,39 +169,31 @@ pub fn recv_events(pid: ContextId, out: &mut [PtraceEvent]) -> Option<usize> {
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Breakpoint {
|
||||
tracee: Arc<WaitCondition>,
|
||||
reached: bool,
|
||||
flags: u8
|
||||
}
|
||||
|
||||
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 mut sessions = sessions_mut();
|
||||
let session = sessions.get_mut(&pid)?;
|
||||
let breakpoint = session.breakpoint.take()?;
|
||||
|
||||
breakpoint.tracee.notify();
|
||||
|
||||
Some(breakpoint)
|
||||
flags: u64
|
||||
}
|
||||
|
||||
/// Continue the process with the specified ID
|
||||
pub fn cont(pid: ContextId) {
|
||||
inner_cont(pid);
|
||||
let mut sessions = sessions_mut();
|
||||
let session = match sessions.get_mut(&pid) {
|
||||
Some(session) => session,
|
||||
None => return
|
||||
};
|
||||
|
||||
// Remove the breakpoint to make sure any yet unreached but
|
||||
// obsolete breakpoints don't stop the program.
|
||||
session.breakpoint = None;
|
||||
|
||||
session.tracee.notify();
|
||||
}
|
||||
|
||||
/// 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()));
|
||||
|
||||
pub fn set_breakpoint(pid: ContextId, flags: u64) {
|
||||
let mut sessions = sessions_mut();
|
||||
let session = sessions.get_mut(&pid).expect("proc (set_breakpoint): invalid session");
|
||||
session.breakpoint = Some(Breakpoint {
|
||||
tracee,
|
||||
reached: false,
|
||||
flags
|
||||
});
|
||||
@@ -244,7 +241,7 @@ pub fn wait(pid: ContextId) -> Result<Option<PtraceEvent>> {
|
||||
|
||||
/// 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(match_flags: u8) -> Option<u8> {
|
||||
pub fn breakpoint_callback(match_flags: u64, event: Option<PtraceEvent>) -> Option<u64> {
|
||||
// Can't hold any locks when executing wait()
|
||||
let (tracee, flags) = {
|
||||
let contexts = context::contexts();
|
||||
@@ -258,10 +255,12 @@ pub fn breakpoint_callback(match_flags: u8) -> Option<u8> {
|
||||
// TODO: How should singlesteps interact with syscalls? How
|
||||
// does Linux handle this?
|
||||
|
||||
if breakpoint.flags & PTRACE_OPERATIONMASK != match_flags & PTRACE_OPERATIONMASK {
|
||||
if breakpoint.flags & match_flags != match_flags {
|
||||
return None;
|
||||
}
|
||||
|
||||
session.events.push_back(event.unwrap_or(ptrace_event!(match_flags)));
|
||||
|
||||
// In case no tracer is waiting, make sure the next one gets
|
||||
// the memo
|
||||
breakpoint.reached = true;
|
||||
@@ -270,7 +269,7 @@ pub fn breakpoint_callback(match_flags: u8) -> Option<u8> {
|
||||
proc_trigger_event(session.file_id, EVENT_WRITE);
|
||||
|
||||
(
|
||||
Arc::clone(&breakpoint.tracee),
|
||||
Arc::clone(&session.tracee),
|
||||
breakpoint.flags
|
||||
)
|
||||
};
|
||||
@@ -280,6 +279,21 @@ pub fn breakpoint_callback(match_flags: u8) -> Option<u8> {
|
||||
Some(flags)
|
||||
}
|
||||
|
||||
/// Obtain the next breakpoint flags for the current process. This is
|
||||
/// used for detecting whether or not the tracer decided to use sysemu
|
||||
/// mode.
|
||||
pub fn next_breakpoint() -> Option<u64> {
|
||||
let contexts = context::contexts();
|
||||
let context = contexts.current()?;
|
||||
let context = context.read();
|
||||
|
||||
let sessions = sessions();
|
||||
let session = sessions.get(&context.id)?;
|
||||
let breakpoint = session.breakpoint.as_ref()?;
|
||||
|
||||
Some(breakpoint.flags)
|
||||
}
|
||||
|
||||
/// Call when a context is closed to alert any tracers
|
||||
pub fn close_tracee(pid: ContextId) -> Option<()> {
|
||||
let mut sessions = sessions_mut();
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
use crate::{
|
||||
arch::paging::VirtualAddress,
|
||||
context::{self, ContextId, Status},
|
||||
context::{self, Context, ContextId, Status},
|
||||
ptrace,
|
||||
scheme::{ATOMIC_SCHEMEID_INIT, AtomicSchemeId, SchemeId},
|
||||
syscall::validate
|
||||
syscall::{
|
||||
data::{FloatRegisters, IntRegisters, PtraceEvent},
|
||||
error::*,
|
||||
flag::*,
|
||||
scheme::Scheme,
|
||||
self,
|
||||
validate,
|
||||
},
|
||||
};
|
||||
|
||||
use alloc::{
|
||||
@@ -17,12 +24,6 @@ use core::{
|
||||
sync::atomic::{AtomicUsize, Ordering}
|
||||
};
|
||||
use spin::{Mutex, RwLock};
|
||||
use syscall::{
|
||||
data::{FloatRegisters, IntRegisters, PtraceEvent},
|
||||
error::*,
|
||||
flag::*,
|
||||
scheme::Scheme
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum RegsKind {
|
||||
@@ -38,6 +39,67 @@ enum Operation {
|
||||
}
|
||||
}
|
||||
|
||||
fn with_context<F, T>(pid: ContextId, callback: F) -> Result<T>
|
||||
where F: FnOnce(&Context) -> Result<T>
|
||||
{
|
||||
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));
|
||||
}
|
||||
callback(&context)
|
||||
}
|
||||
fn with_context_mut<F, T>(pid: ContextId, callback: F) -> Result<T>
|
||||
where F: FnOnce(&mut Context) -> Result<T>
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
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));
|
||||
}
|
||||
callback(&mut context)
|
||||
}
|
||||
fn try_stop_context<F, T>(pid: ContextId, restart_after: bool, mut callback: F) -> Result<T>
|
||||
where F: FnMut(&mut Context) -> Result<T>
|
||||
{
|
||||
let mut first = true;
|
||||
let mut was_stopped = false; // will never be read
|
||||
|
||||
loop {
|
||||
if !first {
|
||||
// We've tried this before, so lets wait before retrying
|
||||
unsafe { context::switch(); }
|
||||
}
|
||||
first = false;
|
||||
|
||||
let contexts = context::contexts();
|
||||
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));
|
||||
}
|
||||
|
||||
// Stop the process until we've done our thing
|
||||
if first {
|
||||
was_stopped = context.ptrace_stop;
|
||||
}
|
||||
context.ptrace_stop = true;
|
||||
|
||||
if context.running {
|
||||
// Process still running, wait until it has stopped
|
||||
continue;
|
||||
}
|
||||
|
||||
let ret = callback(&mut context);
|
||||
|
||||
context.ptrace_stop = restart_after && was_stopped;
|
||||
|
||||
break ret;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Handle {
|
||||
flags: usize,
|
||||
@@ -140,8 +202,10 @@ impl Scheme for ProcScheme {
|
||||
return Err(Error::new(EBUSY));
|
||||
}
|
||||
|
||||
let mut target = target.write();
|
||||
target.ptrace_stop = true;
|
||||
if flags & O_TRUNC == O_TRUNC {
|
||||
let mut target = target.write();
|
||||
target.ptrace_stop = true;
|
||||
}
|
||||
}
|
||||
|
||||
self.handles.write().insert(id, Arc::new(Mutex::new(Handle {
|
||||
@@ -228,42 +292,28 @@ impl Scheme for ProcScheme {
|
||||
float: FloatRegisters,
|
||||
int: IntRegisters
|
||||
}
|
||||
let mut first = true;
|
||||
let (output, size) = loop {
|
||||
if !first {
|
||||
// We've tried this before, so lets wait before retrying
|
||||
unsafe { context::switch(); }
|
||||
}
|
||||
first = false;
|
||||
|
||||
let contexts = context::contexts();
|
||||
let context = contexts.get(handle.pid).ok_or(Error::new(ESRCH))?;
|
||||
let context = context.read();
|
||||
let (output, size) = match kind {
|
||||
RegsKind::Float => with_context(handle.pid, |context| {
|
||||
// NOTE: The kernel will never touch floats
|
||||
|
||||
break match kind {
|
||||
RegsKind::Float => {
|
||||
// NOTE: The kernel will never touch floats
|
||||
|
||||
// In the rare case of not having floating
|
||||
// point registers uninitiated, return
|
||||
// empty everything.
|
||||
let fx = context.arch.get_fx_regs().unwrap_or_default();
|
||||
(Output { float: fx }, mem::size_of::<FloatRegisters>())
|
||||
// In the rare case of not having floating
|
||||
// point registers uninitiated, return
|
||||
// empty everything.
|
||||
let fx = context.arch.get_fx_regs().unwrap_or_default();
|
||||
Ok((Output { float: fx }, mem::size_of::<FloatRegisters>()))
|
||||
})?,
|
||||
RegsKind::Int => try_stop_context(handle.pid, true, |context| match unsafe { ptrace::regs_for(&context) } {
|
||||
None => {
|
||||
println!("{}:{}: Couldn't read registers from stopped process", file!(), line!());
|
||||
Err(Error::new(ENOTRECOVERABLE))
|
||||
},
|
||||
RegsKind::Int => match unsafe { ptrace::regs_for(&context) } {
|
||||
None => {
|
||||
// Another CPU is running this process, wait until it's stopped.
|
||||
continue;
|
||||
},
|
||||
Some(stack) => {
|
||||
let mut regs = IntRegisters::default();
|
||||
|
||||
stack.save(&mut regs);
|
||||
|
||||
(Output { int: regs }, mem::size_of::<IntRegisters>())
|
||||
}
|
||||
Some(stack) => {
|
||||
let mut regs = IntRegisters::default();
|
||||
stack.save(&mut regs);
|
||||
Ok((Output { int: regs }, mem::size_of::<IntRegisters>()))
|
||||
}
|
||||
};
|
||||
})?
|
||||
};
|
||||
|
||||
let bytes = unsafe {
|
||||
@@ -300,7 +350,6 @@ impl Scheme for ProcScheme {
|
||||
let pid = handle.pid;
|
||||
let flags = handle.flags;
|
||||
|
||||
let mut first = true;
|
||||
match handle.operation {
|
||||
Operation::Memory(ref mut offset) => {
|
||||
let contexts = context::contexts();
|
||||
@@ -315,26 +364,16 @@ impl Scheme for ProcScheme {
|
||||
*offset = VirtualAddress::new(offset.get() + buf.len());
|
||||
Ok(buf.len())
|
||||
},
|
||||
Operation::Regs(kind) => loop {
|
||||
if !first {
|
||||
// We've tried this before, so lets wait before retrying
|
||||
unsafe { context::switch(); }
|
||||
}
|
||||
first = false;
|
||||
|
||||
let contexts = context::contexts();
|
||||
let context = contexts.get(handle.pid).ok_or(Error::new(ESRCH))?;
|
||||
let mut context = context.write();
|
||||
|
||||
break match kind {
|
||||
RegsKind::Float => {
|
||||
if buf.len() < mem::size_of::<FloatRegisters>() {
|
||||
return Ok(0);
|
||||
}
|
||||
let regs = unsafe {
|
||||
*(buf as *const _ as *const FloatRegisters)
|
||||
};
|
||||
Operation::Regs(kind) => match kind {
|
||||
RegsKind::Float => {
|
||||
if buf.len() < mem::size_of::<FloatRegisters>() {
|
||||
return Ok(0);
|
||||
}
|
||||
let regs = unsafe {
|
||||
*(buf as *const _ as *const FloatRegisters)
|
||||
};
|
||||
|
||||
with_context_mut(pid, |context| {
|
||||
// NOTE: The kernel will never touch floats
|
||||
|
||||
// Ignore the rare case of floating point
|
||||
@@ -342,82 +381,73 @@ impl Scheme for ProcScheme {
|
||||
let _ = context.arch.set_fx_regs(regs);
|
||||
|
||||
Ok(mem::size_of::<FloatRegisters>())
|
||||
},
|
||||
RegsKind::Int => match unsafe { ptrace::regs_for_mut(&mut context) } {
|
||||
})
|
||||
},
|
||||
RegsKind::Int => {
|
||||
if buf.len() < mem::size_of::<IntRegisters>() {
|
||||
return Ok(0);
|
||||
}
|
||||
let regs = unsafe {
|
||||
*(buf as *const _ as *const IntRegisters)
|
||||
};
|
||||
|
||||
try_stop_context(handle.pid, true, |context| match unsafe { ptrace::regs_for_mut(context) } {
|
||||
None => {
|
||||
// Another CPU is running this process, wait until it's stopped.
|
||||
continue;
|
||||
println!("{}:{}: Couldn't read registers from stopped process", file!(), line!());
|
||||
Err(Error::new(ENOTRECOVERABLE))
|
||||
},
|
||||
Some(stack) => {
|
||||
if buf.len() < mem::size_of::<IntRegisters>() {
|
||||
return Ok(0);
|
||||
}
|
||||
let regs = unsafe {
|
||||
*(buf as *const _ as *const IntRegisters)
|
||||
};
|
||||
|
||||
stack.load(®s);
|
||||
|
||||
Ok(mem::size_of::<IntRegisters>())
|
||||
}
|
||||
}
|
||||
};
|
||||
})
|
||||
}
|
||||
},
|
||||
Operation::Trace { ref mut new_child } => {
|
||||
if buf.len() < 1 {
|
||||
if buf.len() < mem::size_of::<u64>() {
|
||||
return Ok(0);
|
||||
}
|
||||
let op = buf[0];
|
||||
|
||||
let mut blocking = flags & O_NONBLOCK != O_NONBLOCK;
|
||||
let mut singlestep = false;
|
||||
let mut bytes = [0; mem::size_of::<u64>()];
|
||||
let len = bytes.len();
|
||||
bytes.copy_from_slice(&buf[0..len]);
|
||||
let op = u64::from_ne_bytes(bytes);
|
||||
|
||||
match op & PTRACE_OPERATIONMASK {
|
||||
PTRACE_CONT => { ptrace::cont(pid); },
|
||||
PTRACE_SYSCALL | PTRACE_SINGLESTEP | PTRACE_SIGNAL => { // <- not a bitwise OR
|
||||
singlestep = op & PTRACE_OPERATIONMASK == PTRACE_SINGLESTEP;
|
||||
ptrace::set_breakpoint(pid, op);
|
||||
},
|
||||
PTRACE_WAIT => blocking = true,
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
if op & PTRACE_FLAG_WAIT != PTRACE_FLAG_WAIT || op & PTRACE_STOP_MASK != 0 {
|
||||
ptrace::cont(pid);
|
||||
}
|
||||
if op & PTRACE_STOP_MASK != 0 {
|
||||
ptrace::set_breakpoint(pid, op);
|
||||
}
|
||||
|
||||
let mut first = true;
|
||||
loop {
|
||||
if !first {
|
||||
// We've tried this before, so lets wait before retrying
|
||||
unsafe { context::switch(); }
|
||||
}
|
||||
first = false;
|
||||
|
||||
let contexts = context::contexts();
|
||||
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));
|
||||
}
|
||||
|
||||
if singlestep {
|
||||
match unsafe { ptrace::regs_for_mut(&mut context) } {
|
||||
None => continue,
|
||||
Some(stack) => stack.set_singlestep(true)
|
||||
if op & PTRACE_STOP_SINGLESTEP == PTRACE_STOP_SINGLESTEP {
|
||||
try_stop_context(pid, false, |context| {
|
||||
match unsafe { ptrace::regs_for_mut(context) } {
|
||||
// If another CPU is running this process,
|
||||
// await for it to be stopped and in such
|
||||
// a way the registers can be read!
|
||||
None => {
|
||||
println!("{}:{}: Couldn't read registers from stopped process", file!(), line!());
|
||||
Err(Error::new(ENOTRECOVERABLE))
|
||||
},
|
||||
Some(stack) => {
|
||||
stack.set_singlestep(true);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.ptrace_stop = false;
|
||||
break;
|
||||
})?;
|
||||
}
|
||||
|
||||
if blocking {
|
||||
if op & PTRACE_FLAG_WAIT == PTRACE_FLAG_WAIT || flags & O_NONBLOCK != O_NONBLOCK {
|
||||
if let Some(event) = ptrace::wait(pid)? {
|
||||
if event.tag == PTRACE_EVENT_CLONE {
|
||||
*new_child = Some(ContextId::from(unsafe { event.data.clone }));
|
||||
if event.cause == PTRACE_EVENT_CLONE {
|
||||
*new_child = Some(ContextId::from(event.a));
|
||||
}
|
||||
return Ok(0);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(1)
|
||||
Ok(mem::size_of::<u64>())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -467,12 +497,16 @@ impl Scheme for ProcScheme {
|
||||
|
||||
if let Operation::Trace { .. } = handle.operation {
|
||||
ptrace::close_session(handle.pid);
|
||||
}
|
||||
|
||||
let contexts = context::contexts();
|
||||
if let Some(context) = contexts.get(handle.pid) {
|
||||
let mut context = context.write();
|
||||
context.ptrace_stop = false;
|
||||
if handle.flags & O_EXCL == O_EXCL {
|
||||
syscall::kill(handle.pid, SIGKILL)?;
|
||||
} else {
|
||||
let contexts = context::contexts();
|
||||
if let Some(context) = contexts.get(handle.pid) {
|
||||
let mut context = context.write();
|
||||
context.ptrace_stop = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
extern crate syscall;
|
||||
|
||||
pub use self::syscall::{data, error, flag, io, number, scheme};
|
||||
pub use self::syscall::{data, error, flag, io, number, ptrace_event, scheme};
|
||||
|
||||
pub use self::driver::*;
|
||||
pub use self::fs::*;
|
||||
|
||||
@@ -18,17 +18,17 @@ use crate::paging::entry::EntryFlags;
|
||||
use crate::paging::mapper::MapperFlushAll;
|
||||
use crate::paging::temporary_page::TemporaryPage;
|
||||
use crate::paging::{ActivePageTable, InactivePageTable, Page, VirtualAddress, PAGE_SIZE};
|
||||
use crate::ptrace;
|
||||
use crate::{ptrace, syscall};
|
||||
use crate::scheme::FileHandle;
|
||||
use crate::start::usermode;
|
||||
use crate::syscall::data::{PtraceEvent, PtraceEventData, SigAction, Stat};
|
||||
use crate::syscall::data::{PtraceEvent, 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, PTRACE_EVENT_CLONE,
|
||||
SIG_DFL, SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK, SIGCONT, SIGTERM,
|
||||
WCONTINUED, WNOHANG, WUNTRACED, wifcontinued, wifstopped};
|
||||
use crate::syscall::ptrace_event;
|
||||
use crate::syscall::validate::{validate_slice, validate_slice_mut};
|
||||
use crate::syscall;
|
||||
|
||||
pub fn brk(address: usize) -> Result<usize> {
|
||||
let contexts = context::contexts();
|
||||
@@ -585,14 +585,7 @@ 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() {
|
||||
if ptrace::send_event(ptrace_event!(PTRACE_EVENT_CLONE, pid.into())).is_some() {
|
||||
// Freeze the clone, allow ptrace to put breakpoints
|
||||
// to it before it starts
|
||||
let contexts = context::contexts();
|
||||
|
||||
2
syscall
2
syscall
Submodule syscall updated: 9e9f47d2a5...52441c28b1
Reference in New Issue
Block a user