mirror of
https://github.com/hexagonal-sun/moss-kernel.git
synced 2026-01-30 17:11:47 -05:00
ptrace: trace children on fork
When calling `clone()` with PTRACE_O_TRACEFORK set, make the child inherit the current ptrace context. Also start the process with SIGSTOP pending as per the ptrace docs. This enable strace follow-forks functionality `strace -f`.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use super::owned::OwnedTask;
|
||||
use super::ptrace::PTrace;
|
||||
use super::ptrace::{PTrace, TracePoint, ptrace_stop};
|
||||
use super::{ctx::Context, thread_group::signal::SigSet};
|
||||
use crate::kernel::cpu_id::CpuId;
|
||||
use crate::memory::uaccess::copy_to_user;
|
||||
@@ -55,6 +55,10 @@ pub async fn sys_clone(
|
||||
) -> Result<usize> {
|
||||
let flags = CloneFlags::from_bits_truncate(flags);
|
||||
|
||||
// TODO: differentiate between `TracePoint::Fork`, `TracePoint::Clone` and
|
||||
// `TracePoint::VFork`.
|
||||
let should_trace_new_tsk = ptrace_stop(TracePoint::Fork).await;
|
||||
|
||||
let new_task = {
|
||||
let current_task = current_task();
|
||||
|
||||
@@ -128,7 +132,7 @@ pub async fn sys_clone(
|
||||
Arc::new(SpinLock::new(current_task.root.lock_save_irq().clone()))
|
||||
};
|
||||
|
||||
let ptrace = if flags.contains(CloneFlags::CLONE_PTRACE) {
|
||||
let ptrace = if flags.contains(CloneFlags::CLONE_PTRACE) || should_trace_new_tsk {
|
||||
current_task.ptrace.lock_save_irq().clone()
|
||||
} else {
|
||||
PTrace::new()
|
||||
@@ -142,7 +146,14 @@ pub async fn sys_clone(
|
||||
ctx: Context::from_user_ctx(user_ctx),
|
||||
priority: current_task.priority,
|
||||
sig_mask: new_sigmask,
|
||||
pending_signals: SigSet::empty(),
|
||||
pending_signals: if should_trace_new_tsk {
|
||||
// When we want to trace a new task through one of
|
||||
// PTRACE_O_TRACE{FORK,VFORK,CLONE}, stop the child as soon as
|
||||
// it is created.
|
||||
SigSet::SIGSTOP
|
||||
} else {
|
||||
SigSet::empty()
|
||||
},
|
||||
robust_list: None,
|
||||
child_tid_ptr: if !child_tidptr.is_null() {
|
||||
Some(child_tidptr)
|
||||
|
||||
@@ -73,6 +73,7 @@ pub struct PTrace {
|
||||
break_points: TracePoint,
|
||||
state: Option<PTraceState>,
|
||||
waker: Option<Waker>,
|
||||
tracer: Option<Arc<ThreadGroup>>,
|
||||
sysgood: bool,
|
||||
}
|
||||
|
||||
@@ -82,6 +83,7 @@ impl PTrace {
|
||||
state: None,
|
||||
break_points: TracePoint::empty(),
|
||||
waker: None,
|
||||
tracer: None,
|
||||
sysgood: false,
|
||||
}
|
||||
}
|
||||
@@ -146,7 +148,7 @@ impl PTrace {
|
||||
}
|
||||
|
||||
/// Notify parents of a trap event.
|
||||
pub fn notify_parent_of_trap(&self, process: Arc<ThreadGroup>) {
|
||||
pub fn notify_tracer_of_trap(&self, me: &Arc<ThreadGroup>) {
|
||||
let Some(trap_signal) = (match self.state {
|
||||
// For non-signal trace events, we use SIGTRAP.
|
||||
Some(PTraceState::TracePointHit { hit_point, .. }) => match hit_point {
|
||||
@@ -161,21 +163,16 @@ impl PTrace {
|
||||
};
|
||||
|
||||
// Notify the parent that we have stopped (SIGCHLD).
|
||||
if let Some(parent) = process
|
||||
.parent
|
||||
.lock_save_irq()
|
||||
.as_ref()
|
||||
.and_then(|p| p.upgrade())
|
||||
{
|
||||
parent.child_notifiers.child_update(
|
||||
process.tgid,
|
||||
if let Some(tracer) = self.tracer.as_ref() {
|
||||
tracer.child_notifiers.child_update(
|
||||
me.tgid,
|
||||
ChildState::TraceTrap {
|
||||
signal: trap_signal,
|
||||
mask: self.calc_trace_point_mask(),
|
||||
},
|
||||
);
|
||||
|
||||
parent
|
||||
tracer
|
||||
.pending_signals
|
||||
.lock_save_irq()
|
||||
.set_signal(SigId::SIGCHLD);
|
||||
@@ -263,15 +260,15 @@ impl TryFrom<i32> for PtraceOperation {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn ptrace_stop(point: TracePoint) {
|
||||
pub async fn ptrace_stop(point: TracePoint) -> bool {
|
||||
let task_sh = current_task_shared();
|
||||
{
|
||||
let mut ptrace = task_sh.ptrace.lock_save_irq();
|
||||
|
||||
if ptrace.hit_trace_point(point, current_task().ctx.user()) {
|
||||
ptrace.notify_parent_of_trap(task_sh.process.clone());
|
||||
ptrace.notify_tracer_of_trap(&task_sh.process);
|
||||
} else {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,13 +276,13 @@ pub async fn ptrace_stop(point: TracePoint) {
|
||||
let mut ptrace = task_sh.ptrace.lock_save_irq();
|
||||
|
||||
if matches!(ptrace.state, Some(PTraceState::Running)) {
|
||||
Poll::Ready(())
|
||||
Poll::Ready(true)
|
||||
} else {
|
||||
ptrace.set_waker(cx.waker().clone());
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn sys_ptrace(op: i32, pid: u64, addr: UA, data: UA) -> Result<usize> {
|
||||
@@ -296,6 +293,12 @@ pub async fn sys_ptrace(op: i32, pid: u64, addr: UA, data: UA) -> Result<usize>
|
||||
let mut ptrace = current_task.ptrace.lock_save_irq();
|
||||
|
||||
ptrace.state = Some(PTraceState::Running);
|
||||
ptrace.tracer = current_task
|
||||
.process
|
||||
.parent
|
||||
.lock_save_irq()
|
||||
.as_ref()
|
||||
.and_then(|x| x.upgrade());
|
||||
|
||||
// Set default breakpoint for TraceMe.
|
||||
ptrace.break_points = TracePoint::Exec;
|
||||
@@ -361,7 +364,7 @@ pub async fn sys_ptrace(op: i32, pid: u64, addr: UA, data: UA) -> Result<usize>
|
||||
PTraceOptions::PTRACE_O_TRACEEXIT => {
|
||||
ptrace.break_points.insert(TracePoint::Exit)
|
||||
}
|
||||
PTraceOptions::PTRACE_O_TRACEFORK => {
|
||||
PTraceOptions::PTRACE_O_TRACEFORK | PTraceOptions::PTRACE_O_TRACEVFORK => {
|
||||
ptrace.break_points.insert(TracePoint::Fork)
|
||||
}
|
||||
PTraceOptions::PTRACE_O_TRACEEXEC => {
|
||||
|
||||
@@ -251,7 +251,7 @@ pub fn dispatch_userspace_task(ctx: *mut UserCtx) {
|
||||
while let Some(signal) = task.take_signal() {
|
||||
let mut ptrace = task.ptrace.lock_save_irq();
|
||||
if ptrace.trace_signal(signal, task.ctx.user()) {
|
||||
ptrace.notify_parent_of_trap(task.process.clone());
|
||||
ptrace.notify_tracer_of_trap(&task.process);
|
||||
ptrace.set_waker(create_waker(task.descriptor()));
|
||||
|
||||
*task.state.lock_save_irq() = TaskState::Stopped;
|
||||
|
||||
Reference in New Issue
Block a user