-
Notifications
You must be signed in to change notification settings - Fork 126
feat(starry-kernel): add PTRACE_SEIZE flow and PTRACE_LISTEN for strace support #1323
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
21fea86
8e7691c
46936df
6dd373a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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( | ||
|
|
@@ -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; | ||
|
|
@@ -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), | ||
| } | ||
| } | ||
|
|
@@ -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); | ||
| } | ||
|
|
@@ -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 { | ||
|
|
@@ -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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (非阻塞建议)此处 |
||
| | PTRACE_O_TRACEVFORKDONE | ||
| | PTRACE_O_TRACEEXIT | ||
| | PTRACE_O_TRACESECCOMP | ||
| | PTRACE_O_EXITKILL; | ||
| if data & !valid_mask == 0 { | ||
| tracee.set_ptrace_options(data); | ||
| } | ||
| } | ||
| Ok(0) | ||
| } | ||
|
|
||
|
|
@@ -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, | ||
|
|
@@ -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", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这段 pre-execve stop 逻辑依赖 |
||
| 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(), | ||
|
|
@@ -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) => { | ||
|
|
||
| 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) |
There was a problem hiding this comment.
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或类似名称,避免两处维护不一致的风险。当前两者内容一致,暂不阻塞。