diff --git a/sys/amd64/linux/linux_ptrace.c b/sys/amd64/linux/linux_ptrace.c --- a/sys/amd64/linux/linux_ptrace.c +++ b/sys/amd64/linux/linux_ptrace.c @@ -70,7 +70,9 @@ #define LINUX_PTRACE_GETSIGINFO 0x4202 #define LINUX_PTRACE_GETREGSET 0x4204 #define LINUX_PTRACE_SEIZE 0x4206 -#define LINUX_PTRACE_GET_SYSCALL_INFO 0x420e +#define LINUX_PTRACE_INTERRUPT 0x4207 +#define LINUX_PTRACE_LISTEN 0x4208 +#define LINUX_PTRACE_GET_SYSCALL_INFO 0x420e #define LINUX_PTRACE_EVENT_EXIT 6 @@ -577,14 +579,6 @@ } } -static int -linux_ptrace_seize(struct thread *td, pid_t pid, l_ulong addr, l_ulong data) -{ - - linux_msg(td, "PTRACE_SEIZE not implemented; returning EINVAL"); - return (EINVAL); -} - static int linux_ptrace_get_syscall_info(struct thread *td, pid_t pid, l_ulong addr, l_ulong data) { @@ -701,11 +695,18 @@ error = linux_ptrace_getregset(td, pid, uap->addr, uap->data); break; case LINUX_PTRACE_SEIZE: - error = linux_ptrace_seize(td, pid, uap->addr, uap->data); + error = linux_ptrace_setoptions(td, pid, uap->data); + error = kern_ptrace(td, PT_SEIZE, pid, addr, uap->data); + break; + case LINUX_PTRACE_INTERRUPT: + error = kern_ptrace(td, PT_INTERRUPT, pid, addr, uap->data); break; - case LINUX_PTRACE_GET_SYSCALL_INFO: - error = linux_ptrace_get_syscall_info(td, pid, uap->addr, uap->data); + case LINUX_PTRACE_LISTEN: + error = kern_ptrace(td, PT_LISTEN, pid, addr, uap->data); break; +// case LINUX_PTRACE_GET_SYSCALL_INFO: +// error = linux_ptrace_get_syscall_info(td, pid, uap->addr, uap->data); +// break; default: linux_msg(td, "ptrace(%ld, ...) not implemented; " "returning EINVAL", uap->req); diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -2884,6 +2884,20 @@ } } + + if ((p->p_flag & P_TRACED) != 0 && + (p->p_flag2 & P2_SEIZED) != 0 && + SIGISMEMBER(sigpending, SIGKILL)) { + /* + * If we are seized: linux behavior is to honor + * the SIGKILL request without first sending a + * STOP signal to the process being debugged. + */ + sig = SIGKILL; + //proc_wkilled(p); + return (SIGKILL); + } + if ((p->p_flag & (P_TRACED | P_PPTRACE)) == P_TRACED && (p->p_flag2 & P2_PTRACE_FSTP) != 0 && SIGISMEMBER(sigpending, SIGSTOP)) { diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -623,6 +623,8 @@ /* Lock proctree before locking the process. */ switch (req) { case PT_TRACE_ME: + case PT_INTERRUPT: + case PT_SEIZE: case PT_ATTACH: case PT_STEP: case PT_CONTINUE: @@ -709,6 +711,17 @@ * Permissions check */ switch (req) { + case PT_INTERRUPT: + case PT_LISTEN: + /* + * Only works on tracees attached by PT_SEIZE. + */ + if ((p->p_flag2 & P2_SEIZED) == 0) { + error = EINVAL; + goto fail; + } + break; + case PT_TRACE_ME: /* * Always legal, when there is a parent process which @@ -724,6 +737,7 @@ } break; + case PT_SEIZE: case PT_ATTACH: /* Self */ if (p == td->td_proc) { @@ -806,6 +820,52 @@ CTR1(KTR_PTRACE, "PT_TRACE_ME: pid %d", p->p_pid); break; + case PT_SEIZE: + proc_set_traced(p, false); + proc_reparent(p, td->td_proc, false); + CTR2(KTR_PTRACE, "PT_ATTACH: pid %d, oppid %d", p->p_pid, + p->p_oppid); + + sx_xunlock(&proctree_lock); + proctree_locked = false; + MPASS(p->p_xthread == NULL); + MPASS((p->p_flag & P_STOPPED_TRACE) == 0); + + p->p_flag2 |= P2_SEIZED; + + /* + * XXX Do we actually have to do anything here? + */ + break; + + case PT_INTERRUPT: + sx_xunlock(&proctree_lock); + proctree_locked = false; + MPASS(p->p_xthread == NULL); + MPASS((p->p_flag & P_STOPPED_TRACE) == 0); + + /* + * If already stopped due to a stop signal, clear the + * existing stop before triggering a traced SIGSTOP. + */ + if ((p->p_flag & P_STOPPED_SIG) != 0) { + PROC_SLOCK(p); + p->p_flag &= ~(P_STOPPED_SIG | P_WAITED); + thread_unsuspend(p); + PROC_SUNLOCK(p); + } + + kern_psignal(p, SIGSTOP); + break; + + case PT_LISTEN: + // XXX not sure + td2->td_dbgflags |= TDB_SUSPEND; + thread_lock(td2); + td2->td_flags |= TDF_NEEDSUSPCHK; + thread_unlock(td2); + break; + case PT_ATTACH: /* security check done above */ /* @@ -817,7 +877,7 @@ * The old parent is remembered so we can put things back * on a "detach". */ - proc_set_traced(p, true); + proc_set_traced(p, req == PT_ATTACH); proc_reparent(p, td->td_proc, false); CTR2(KTR_PTRACE, "PT_ATTACH: pid %d, oppid %d", p->p_pid, p->p_oppid); diff --git a/sys/sys/proc.h b/sys/sys/proc.h --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -813,6 +813,7 @@ MAP_STACK */ #define P2_STKGAP_DISABLE_EXEC 0x00001000 /* Stack gap disabled after exec */ +#define P2_SEIZED 0x00008000 /* Linux behaviour 'seize'. */ /* Flags protected by proctree_lock, kept in p_treeflags. */ #define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */ diff --git a/sys/sys/ptrace.h b/sys/sys/ptrace.h --- a/sys/sys/ptrace.h +++ b/sys/sys/ptrace.h @@ -83,6 +83,9 @@ #define PT_VM_TIMESTAMP 40 /* Get VM version (timestamp) */ #define PT_VM_ENTRY 41 /* Get VM map (entry) */ +#define PT_SEIZE 42 /* like attach but do not stop */ +#define PT_INTERRUPT 43 /* */ +#define PT_LISTEN 44 /* */ #define PT_FIRSTMACH 64 /* for machine-specific requests */ #include /* machine-specific requests, if any */