Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions os/StarryOS/kernel/src/syscall/task/execve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,14 @@ fn do_execve(
proc_data.proc.rename_thread(my_tid, tgid);
}

// All ptrace tracees (both TRACEME and ATTACH) unconditionally
// stop with SIGTRAP on execve (Linux ptrace(2)). PTRACE_O_TRACEEXEC
// only controls whether the stop carries PTRACE_EVENT_EXEC data,
// not whether the stop itself occurs.
if proc_data.is_ptrace_traceme() || proc_data.is_ptrace_attached() {
proc_data.set_ptrace_exec_stop_pending();
}

// Reset every user-visible register to a fresh-process state, not
// just IP/SP. Linux's `start_thread()` clears all GP registers,
// resets the TLS pointer, and clobbers any FP/SIMD state to the
Expand All @@ -407,14 +415,6 @@ fn do_execve(
has_ldso,
);

// All ptrace tracees (both TRACEME and ATTACH) unconditionally
// stop with SIGTRAP on execve (Linux ptrace(2)). PTRACE_O_TRACEEXEC
// only controls whether the stop carries PTRACE_EVENT_EXEC data,
// not whether the stop itself occurs.
if proc_data.is_ptrace_traceme() || proc_data.is_ptrace_attached() {
proc_data.set_ptrace_exec_stop_pending();
}

// Unblock a vfork parent waiting for this child to exec.
// Must be last: by now CLOEXEC fds are closed so the parent's pipe
// read will see EOF correctly.
Expand Down
51 changes: 48 additions & 3 deletions os/StarryOS/kernel/src/syscall/task/ptrace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const PTRACE_GETREGSET: u32 = 0x4204;
const PTRACE_SETREGSET: u32 = 0x4205;
const PTRACE_SEIZE: u32 = 0x4206;
const PTRACE_INTERRUPT: u32 = 0x4207;
const PTRACE_LISTEN: u32 = 0x4208;

const NT_PRSTATUS: usize = 1;
#[cfg(any(
Expand All @@ -76,13 +77,16 @@ const PTRACE_O_TRACECLONE: usize = 1 << 3;
const PTRACE_O_TRACEEXEC: usize = 1 << 4;
const PTRACE_O_TRACEVFORKDONE: usize = 1 << 5;
const PTRACE_O_TRACEEXIT: usize = 1 << 6;
const PTRACE_O_TRACESECCOMP: usize = 1 << 7;
const PTRACE_O_EXITKILL: usize = 1 << 20;

pub const PTRACE_EVENT_FORK: u32 = 1;
pub const PTRACE_EVENT_VFORK: u32 = 2;
pub const PTRACE_EVENT_CLONE: u32 = 3;
const PTRACE_EVENT_EXEC: u32 = 4;
pub const PTRACE_EVENT_VFORK_DONE: u32 = 5;
const PTRACE_EVENT_EXIT: u32 = 6;
const PTRACE_EVENT_STOP: u32 = 128;

#[cfg(target_arch = "riscv64")]
const EBREAK_INSN: u16 = 0x9002;
Expand Down Expand Up @@ -260,8 +264,9 @@ pub fn sys_ptrace(request: u32, pid: usize, addr: usize, data: usize) -> AxResul
PTRACE_SETSIGINFO => ptrace_setsiginfo(pid, data),
PTRACE_GETREGSET => ptrace_getregset(pid, addr, data),
PTRACE_SETREGSET => ptrace_setregset(pid, addr, data),
PTRACE_SEIZE => ptrace_seize(pid, addr),
PTRACE_SEIZE => ptrace_seize(pid, addr, data),
PTRACE_INTERRUPT => ptrace_interrupt(pid),
PTRACE_LISTEN => ptrace_listen(pid),
_ => Err(AxError::Unsupported),
}
}
Expand Down Expand Up @@ -376,7 +381,9 @@ fn ptrace_setoptions(pid: usize, options: usize) -> AxResult<isize> {
| PTRACE_O_TRACECLONE
| PTRACE_O_TRACEEXEC
| PTRACE_O_TRACEVFORKDONE
| PTRACE_O_TRACEEXIT;
| PTRACE_O_TRACEEXIT
| PTRACE_O_TRACESECCOMP
| PTRACE_O_EXITKILL;
if options & !valid_mask != 0 {
return Err(AxError::InvalidInput);
}
Expand Down Expand Up @@ -598,7 +605,7 @@ fn ptrace_setfpregs(pid: usize, data: usize) -> AxResult<isize> {
Err(AxError::Unsupported)
}

fn ptrace_seize(pid: usize, _addr: usize) -> AxResult<isize> {
fn ptrace_seize(pid: usize, _addr: usize, data: usize) -> AxResult<isize> {
let tracer_pid = current().as_thread().proc_data.proc.pid();
let tracee_pid = Pid::try_from(pid).map_err(|_| AxError::from(LinuxError::ESRCH))?;
if tracee_pid == tracer_pid {
Expand All @@ -613,6 +620,22 @@ fn ptrace_seize(pid: usize, _addr: usize) -> AxResult<isize> {
}
tracee.set_ptrace_tracer_pid(tracer_pid);
tracee.set_ptrace_attached();
// PTRACE_SEIZE accepts ptrace options in the data argument;
// store them immediately (strace passes ptrace_setoptions here).
if data != 0 {
let valid_mask = PTRACE_O_TRACESYSGOOD
| PTRACE_O_TRACEFORK
| PTRACE_O_TRACEVFORK
| PTRACE_O_TRACECLONE
| PTRACE_O_TRACEEXEC

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SEIZE 时存储 options 的 valid_mask 与 SETOPTIONS 中的重复定义。建议提取为常量 SEIZE_VALID_OPTIONS 或类似名称,避免两处维护不一致的风险。当前两者内容一致,暂不阻塞。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(非阻塞建议)此处 valid_maskptrace_setoptions 中的定义完全一致。建议提取为模块级常量 const SEIZE_VALID_OPTIONS: usize = ...;,两处共享引用,避免后续新增 option 时只改一处遗漏另一处。

| PTRACE_O_TRACEVFORKDONE
| PTRACE_O_TRACEEXIT
| PTRACE_O_TRACESECCOMP
| PTRACE_O_EXITKILL;
if data & !valid_mask == 0 {
tracee.set_ptrace_options(data);
}
}
Ok(0)
}

Expand Down Expand Up @@ -653,6 +676,10 @@ fn ptrace_interrupt(pid: usize) -> AxResult<isize> {
if tracee.ptrace_stop_signo().is_some() {
return Ok(0);
}
// Tag the upcoming stop with PTRACE_EVENT_STOP so the tracer sees
// EVENT_STOP(128) in the upper 16 bits of the wait status and
// handles it as a group-stop (TE_GROUP_STOP / TE_RESTART).
tracee.set_ptrace_pending_event(tracee_pid, PTRACE_EVENT_STOP, 0);
use starry_signal::SignalInfo;
let _ = crate::task::send_signal_to_process(
tracee_pid,
Expand All @@ -661,6 +688,24 @@ fn ptrace_interrupt(pid: usize) -> AxResult<isize> {
Ok(0)
}

#[cfg(any(
target_arch = "riscv64",
target_arch = "aarch64",
target_arch = "loongarch64",
target_arch = "x86_64"
))]
fn ptrace_listen(pid: usize) -> AxResult<isize> {
let (tracee, tid) = ptrace_stopped_tracee_with_tid(pid)?;
let signo = tracee
.ptrace_stop_signo_for(tid)
.ok_or_else(|| AxError::from(LinuxError::ESRCH))?;
tracee.set_ptrace_singlestep_for(tid, false);
tracee.set_ptrace_syscall_trace_for(tid, false);
tracee.resume_ptrace_stop_with_signal_for(tid, signo as u32);
ax_task::yield_now();
Ok(0)
}

#[cfg(any(
target_arch = "riscv64",
target_arch = "aarch64",
Expand Down
8 changes: 8 additions & 0 deletions os/StarryOS/kernel/src/task/signal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,14 @@ pub fn check_signals(

let signo = sig.signo();

// Signals whose disposition is Continue (SIGCONT) or that are
// unconditionally forwarded (SIGKILL, already checked above) must
// never create a ptrace-stop — they are consumed silently so that
// tracers observe only "real" stops.
if matches!(os_action, SignalOSAction::Continue) {
return true;
}

if signo != Signo::SIGKILL
&& !thr
.proc_data
Expand Down
48 changes: 36 additions & 12 deletions os/StarryOS/kernel/src/task/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,24 @@ pub fn new_user_task(name: &str, mut uctx: UserContext, set_child_tid: usize) ->
}
}

// Linux stops tracees *before* execve so the tracer
// can read the original syscall arguments (path, argv,
// envp) from the trap frame. When no syscall-trace was
// active (trace_state != Entry) we must create that
// pre-execve stop here. With syscall-trace active the
// entry-stop above already covered it.
if !matches!(trace_state, SyscallTraceState::Entry)
&& (thr.proc_data.is_ptrace_traceme()
|| thr.proc_data.is_ptrace_attached())
&& matches!(
Sysno::new(saved_sysno),
Some(Sysno::execve | Sysno::execveat)
)
{
crate::syscall::ptrace_notify_exec(thr.proc_data.proc.pid());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这段 pre-execve stop 逻辑依赖 Sysno::new(saved_sysno) 匹配 execve | execveat,但 saved_sysno 是在 entry 时保存的。如果内核支持其他能触发 exec 的 syscall(如 fexecve 的未来变体),这里需要同步更新。当前范围足够,仅作提示。

let _ = ptrace_stop_current(thr, Signo::SIGTRAP, &mut uctx);
}

if let Some(exit_code) = ptrace_exit_event_code(saved_sysno, saved_a0)
&& crate::syscall::ptrace_notify_exit(
thr.proc_data.proc.pid(),
Expand All @@ -81,20 +99,26 @@ pub fn new_user_task(name: &str, mut uctx: UserContext, set_child_tid: usize) ->
{
continue;
}
if matches!(
thr.proc_data.take_ptrace_syscall_trace_for(tid),
SyscallTraceState::Exit
) {
let _ = ptrace_syscall_stop_current(thr, Signo::SIGTRAP, &mut uctx);
}
if thr.proc_data.take_ptrace_exec_stop_pending() {
let _is_event =
let exec_pending = thr.proc_data.take_ptrace_exec_stop_pending();
let syscall_trace = thr.proc_data.take_ptrace_syscall_trace_for(tid);
match (exec_pending, syscall_trace) {
// Normal syscall exit — non-execve path.
(false, SyscallTraceState::Exit) => {
let _ = ptrace_syscall_stop_current(thr, Signo::SIGTRAP, &mut uctx);
}
// Execve with syscall-trace active (ATTACH/SEIZE),
// or enabled during the pre-execve stop (TRACEME +
// PTRACE_SYSCALL). Deliver the exit-stop with the
// return value and the PTRACE_EVENT_EXEC event.
(true, SyscallTraceState::Exit | SyscallTraceState::Entry) => {
crate::syscall::ptrace_notify_exec(thr.proc_data.proc.pid());
if let Some(_resume_sig) =
ptrace_stop_current(thr, Signo::SIGTRAP, &mut uctx)
{
continue;
let _ = ptrace_syscall_stop_current(thr, Signo::SIGTRAP, &mut uctx);
}
// Execve after pre-execve stop, but tracer used
// PTRACE_CONT (no syscall-trace). The pre-execve
// stop already notified the tracer; no extra stop.
(true, _) => {}
(false, _) => {}
}
}
ReturnReason::PageFault(addr, flags) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 3.20)
project(test-ptrace-listen C)
include(${CMAKE_CURRENT_LIST_DIR}/../common/starry_arch_filter.cmake)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)

starry_arch_filtered_executable(test-ptrace-listen "riscv64" "test-ptrace-listen skipped on unsupported architecture" src/main.c)
target_include_directories(test-ptrace-listen PRIVATE src)
target_compile_options(test-ptrace-listen PRIVATE -Wall -Wextra -Werror)

install(TARGETS test-ptrace-listen RUNTIME DESTINATION usr/bin/starry-test-suit)
Loading
Loading