Changeset View
Standalone View
sys/kern/sys_process.c
Show First 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | |||||
#include <sys/priv.h> | #include <sys/priv.h> | ||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/vnode.h> | #include <sys/vnode.h> | ||||
#include <sys/ptrace.h> | #include <sys/ptrace.h> | ||||
#include <sys/rwlock.h> | #include <sys/rwlock.h> | ||||
#include <sys/sx.h> | #include <sys/sx.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/signalvar.h> | #include <sys/signalvar.h> | ||||
#include <sys/caprights.h> | |||||
#include <sys/filedesc.h> | |||||
#include <machine/reg.h> | #include <machine/reg.h> | ||||
#include <security/audit/audit.h> | #include <security/audit/audit.h> | ||||
#include <vm/vm.h> | #include <vm/vm.h> | ||||
#include <vm/pmap.h> | #include <vm/pmap.h> | ||||
#include <vm/vm_extern.h> | #include <vm/vm_extern.h> | ||||
▲ Show 20 Lines • Show All 402 Lines • ▼ Show 20 Lines | sys_ptrace(struct thread *td, struct ptrace_args *uap) | ||||
/* | /* | ||||
* XXX this obfuscation is to reduce stack usage, but the register | * XXX this obfuscation is to reduce stack usage, but the register | ||||
* structs may be too large to put on the stack anyway. | * structs may be too large to put on the stack anyway. | ||||
*/ | */ | ||||
union { | union { | ||||
struct ptrace_io_desc piod; | struct ptrace_io_desc piod; | ||||
struct ptrace_lwpinfo pl; | struct ptrace_lwpinfo pl; | ||||
struct ptrace_vm_entry pve; | struct ptrace_vm_entry pve; | ||||
struct ptrace_coredump pc; | |||||
struct dbreg dbreg; | struct dbreg dbreg; | ||||
struct fpreg fpreg; | struct fpreg fpreg; | ||||
struct reg reg; | struct reg reg; | ||||
char args[sizeof(td->td_sa.args)]; | char args[sizeof(td->td_sa.args)]; | ||||
struct ptrace_sc_ret psr; | struct ptrace_sc_ret psr; | ||||
int ptevents; | int ptevents; | ||||
} r; | } r; | ||||
void *addr; | void *addr; | ||||
Show All 34 Lines | else | ||||
error = copyin(uap->addr, &r.ptevents, uap->data); | error = copyin(uap->addr, &r.ptevents, uap->data); | ||||
break; | break; | ||||
case PT_IO: | case PT_IO: | ||||
error = copyin(uap->addr, &r.piod, sizeof(r.piod)); | error = copyin(uap->addr, &r.piod, sizeof(r.piod)); | ||||
break; | break; | ||||
case PT_VM_ENTRY: | case PT_VM_ENTRY: | ||||
error = copyin(uap->addr, &r.pve, sizeof(r.pve)); | error = copyin(uap->addr, &r.pve, sizeof(r.pve)); | ||||
break; | break; | ||||
case PT_COREDUMP: | |||||
kib: This needs COMPAT32 handling | |||||
if (uap->data != sizeof(r.pc)) | |||||
error = EINVAL; | |||||
else | |||||
error = copyin(uap->addr, &r.pc, uap->data); | |||||
if (r.pc.pc_flags != 0) | |||||
error = EINVAL; | |||||
break; | |||||
default: | default: | ||||
addr = uap->addr; | addr = uap->addr; | ||||
break; | break; | ||||
} | } | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
error = kern_ptrace(td, uap->req, uap->pid, addr, uap->data); | error = kern_ptrace(td, uap->req, uap->pid, addr, uap->data); | ||||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
struct iovec iov; | struct iovec iov; | ||||
struct uio uio; | struct uio uio; | ||||
struct proc *curp, *p, *pp; | struct proc *curp, *p, *pp; | ||||
struct thread *td2 = NULL, *td3; | struct thread *td2 = NULL, *td3; | ||||
struct ptrace_io_desc *piod = NULL; | struct ptrace_io_desc *piod = NULL; | ||||
struct ptrace_lwpinfo *pl; | struct ptrace_lwpinfo *pl; | ||||
struct ptrace_sc_ret *psr; | struct ptrace_sc_ret *psr; | ||||
struct file *fp; | |||||
int error, num, tmp; | int error, num, tmp; | ||||
int proctree_locked = 0; | int proctree_locked = 0; | ||||
lwpid_t tid = 0, *buf; | lwpid_t tid = 0, *buf; | ||||
#ifdef COMPAT_FREEBSD32 | #ifdef COMPAT_FREEBSD32 | ||||
int wrap32 = 0, safe = 0; | int wrap32 = 0, safe = 0; | ||||
#endif | #endif | ||||
curp = td->td_proc; | curp = td->td_proc; | ||||
/* Lock proctree before locking the process. */ | /* Lock proctree before locking the process. */ | ||||
switch (req) { | switch (req) { | ||||
case PT_TRACE_ME: | case PT_TRACE_ME: | ||||
case PT_ATTACH: | case PT_ATTACH: | ||||
case PT_STEP: | case PT_STEP: | ||||
case PT_CONTINUE: | case PT_CONTINUE: | ||||
case PT_TO_SCE: | case PT_TO_SCE: | ||||
case PT_TO_SCX: | case PT_TO_SCX: | ||||
case PT_SYSCALL: | case PT_SYSCALL: | ||||
case PT_FOLLOW_FORK: | case PT_FOLLOW_FORK: | ||||
case PT_LWP_EVENTS: | case PT_LWP_EVENTS: | ||||
case PT_GET_EVENT_MASK: | case PT_GET_EVENT_MASK: | ||||
case PT_SET_EVENT_MASK: | case PT_SET_EVENT_MASK: | ||||
case PT_DETACH: | case PT_DETACH: | ||||
case PT_GET_SC_ARGS: | case PT_GET_SC_ARGS: | ||||
case PT_COREDUMP: | |||||
sx_xlock(&proctree_lock); | sx_xlock(&proctree_lock); | ||||
proctree_locked = 1; | proctree_locked = 1; | ||||
break; | break; | ||||
default: | default: | ||||
break; | break; | ||||
} | } | ||||
if (req == PT_TRACE_ME) { | if (req == PT_TRACE_ME) { | ||||
▲ Show 20 Lines • Show All 299 Lines • ▼ Show 20 Lines | #endif | ||||
break; | break; | ||||
case PT_STEP: | case PT_STEP: | ||||
case PT_CONTINUE: | case PT_CONTINUE: | ||||
case PT_TO_SCE: | case PT_TO_SCE: | ||||
case PT_TO_SCX: | case PT_TO_SCX: | ||||
case PT_SYSCALL: | case PT_SYSCALL: | ||||
case PT_DETACH: | case PT_DETACH: | ||||
case PT_COREDUMP: | |||||
/* PT_COREDUMP does not pass signal in data. */ | |||||
if (req == PT_COREDUMP) | |||||
data = 0; | |||||
/* Zero means do not send any signal */ | /* Zero means do not send any signal */ | ||||
if (data < 0 || data > _SIG_MAXSIG) { | if (data < 0 || data > _SIG_MAXSIG) { | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
switch (req) { | switch (req) { | ||||
case PT_STEP: | case PT_STEP: | ||||
Show All 37 Lines | case PT_SYSCALL: | ||||
break; | break; | ||||
case PT_CONTINUE: | case PT_CONTINUE: | ||||
CTR3(KTR_PTRACE, | CTR3(KTR_PTRACE, | ||||
"PT_CONTINUE: pid %d, PC = %#lx, sig = %d", | "PT_CONTINUE: pid %d, PC = %#lx, sig = %d", | ||||
p->p_pid, (u_long)(uintfptr_t)addr, data); | p->p_pid, (u_long)(uintfptr_t)addr, data); | ||||
break; | break; | ||||
} | } | ||||
break; | break; | ||||
case PT_COREDUMP: | |||||
{ | |||||
struct ptrace_coredump *pc = addr; | |||||
struct thread_coredump *coredump_data; | |||||
CTR2(KTR_PTRACE, | |||||
Done Inline ActionsIndent should be +4. kib: Indent should be +4. | |||||
"PT_COREDUMP: pid %d, fd = %d", | |||||
p->p_pid, pc->pc_fd); | |||||
if (td2->td_dbgflags & TDB_COREDUMP) { | |||||
error = EBUSY; | |||||
break; | |||||
} | |||||
error = getvnode(td, pc->pc_fd, &cap_write_rights, &fp); | |||||
if (error != 0) | |||||
break; | |||||
/* Do not try writing to non-regular files. */ | |||||
if (fp->f_vnode->v_type != VREG) { | |||||
error = EFAULT; | |||||
fdrop(fp, td); | |||||
break; | |||||
} | |||||
PROC_UNLOCK(p); | |||||
Not Done Inline ActionsNote that somebody else might have detached the target while we unlocked the process. Same holds for the wait for coredump to finish. I will handle this. kib: Note that somebody else might have detached the target while we unlocked the process. Same… | |||||
coredump_data = malloc(sizeof(*td2->td_coredump), | |||||
M_SUBPROC, M_WAITOK | M_ZERO); | |||||
PROC_LOCK(p); | |||||
Done Inline Actionscoredump_data cannot be NULL due to M_WAITOK kib: coredump_data cannot be NULL due to M_WAITOK | |||||
coredump_data->tc_vn = fp->f_vnode; | |||||
coredump_data->tc_limit = pc->pc_limit <= 0 ? | |||||
OFF_MAX : pc->pc_limit; | |||||
td2->td_coredump = coredump_data; | |||||
td2->td_dbgflags |= TDB_COREDUMP; | |||||
thread_lock(td); | |||||
kibUnsubmitted Not Done Inline ActionsIt should be td2, not td. kib: It should be td2, not td. | |||||
td2->td_flags |= TDF_ASTPENDING; | |||||
Done Inline ActionsStyle: no need for (), '?' should be on this line, not on the continuation one. kib: Style: no need for (), '?' should be on this line, not on the continuation one. | |||||
thread_unlock(td); | |||||
break; | |||||
Done Inline Actionsno need for () td_flags is locked by thread lock. kib: no need for ()
td_flags is locked by thread lock. | |||||
} | |||||
case PT_DETACH: | case PT_DETACH: | ||||
/* | /* | ||||
* Clear P_TRACED before reparenting | * Clear P_TRACED before reparenting | ||||
* a detached process back to its original | * a detached process back to its original | ||||
* parent. Otherwise the debugee will be set | * parent. Otherwise the debugee will be set | ||||
* as an orphan of the debugger. | * as an orphan of the debugger. | ||||
*/ | */ | ||||
p->p_flag &= ~(P_TRACED | P_WAITED); | p->p_flag &= ~(P_TRACED | P_WAITED); | ||||
Show All 33 Lines | case PT_DETACH: | ||||
p->p_flag2 &= ~P2_PTRACE_FSTP; | p->p_flag2 &= ~P2_PTRACE_FSTP; | ||||
} | } | ||||
/* should we send SIGCHLD? */ | /* should we send SIGCHLD? */ | ||||
/* childproc_continued(p); */ | /* childproc_continued(p); */ | ||||
break; | break; | ||||
} | } | ||||
if (error != 0) | |||||
break; | |||||
sx_xunlock(&proctree_lock); | sx_xunlock(&proctree_lock); | ||||
proctree_locked = 0; | proctree_locked = 0; | ||||
sendsig: | sendsig: | ||||
MPASS(proctree_locked == 0); | MPASS(proctree_locked == 0); | ||||
/* | /* | ||||
Done Inline ActionsI do not see why this assert must held. kib: I do not see why this assert must held. | |||||
* Clear the pending event for the thread that just | * Clear the pending event for the thread that just | ||||
Done Inline ActionsE.g. return EBUSY. kib: E.g. return EBUSY. | |||||
* reported its event (p_xthread). This may not be | * reported its event (p_xthread). This may not be | ||||
* the thread passed to PT_CONTINUE, PT_STEP, etc. if | * the thread passed to PT_CONTINUE, PT_STEP, etc. if | ||||
Done Inline ActionsI think you need to inform the victim about AST pending. kib: I think you need to inform the victim about AST pending. | |||||
* the debugger is resuming a different thread. | * the debugger is resuming a different thread. | ||||
* | * | ||||
* Deliver any pending signal via the reporting thread. | * Deliver any pending signal via the reporting thread. | ||||
*/ | */ | ||||
MPASS(p->p_xthread != NULL); | MPASS(p->p_xthread != NULL); | ||||
p->p_xthread->td_dbgflags &= ~TDB_XSIG; | p->p_xthread->td_dbgflags &= ~TDB_XSIG; | ||||
p->p_xthread->td_xsig = data; | p->p_xthread->td_xsig = data; | ||||
p->p_xthread = NULL; | p->p_xthread = NULL; | ||||
▲ Show 20 Lines • Show All 235 Lines • ▼ Show 20 Lines | #ifdef __HAVE_PTRACE_MACHDEP | ||||
} else | } else | ||||
#endif | #endif | ||||
/* Unknown request. */ | /* Unknown request. */ | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
out: | out: | ||||
if (req == PT_COREDUMP && error != 0) { | |||||
void *coredump_data; | |||||
Done Inline ActionsStyle: empty line after locals declaration. Again, td_flags is synchronized by thread lock. This makes me think that td_dbgflags and TDB_COREDUMP would be better choice. kib: Style: empty line after locals declaration.
Again, td_flags is synchronized by thread lock. | |||||
while ((td2->td_dbgflags & TDB_COREDUMP) != 0) | |||||
msleep(p, &p->p_mtx, PPAUSE, "crdmp", 0); | |||||
Done Inline ActionsMPASS(td2->td_coredump != NULL); kib: MPASS(td2->td_coredump != NULL); | |||||
MPASS(td2->td_coredump != NULL); | |||||
error = td2->td_coredump->tc_error; | |||||
fdrop(fp, td); | |||||
coredump_data = td2->td_coredump; | |||||
td2->td_coredump = NULL; | |||||
Done Inline ActionsI think locking would be still right if you just free() under the process lock. kib: I think locking would be still right if you just free() under the process lock. | |||||
free(coredump_data, M_SUBPROC); | |||||
} | |||||
/* Drop our hold on this process now that the request has completed. */ | /* Drop our hold on this process now that the request has completed. */ | ||||
_PRELE(p); | _PRELE(p); | ||||
fail: | fail: | ||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | ||||
if (proctree_locked) | if (proctree_locked) | ||||
sx_xunlock(&proctree_lock); | sx_xunlock(&proctree_lock); | ||||
return (error); | return (error); | ||||
} | } | ||||
#undef PROC_READ | #undef PROC_READ | ||||
#undef PROC_WRITE | #undef PROC_WRITE |
This needs COMPAT32 handling