Changeset View
Standalone View
sys/kern/kern_ktrace.c
Show First 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | ||||||||||
#include <sys/kthread.h> | #include <sys/kthread.h> | |||||||||
#include <sys/lock.h> | #include <sys/lock.h> | |||||||||
#include <sys/mutex.h> | #include <sys/mutex.h> | |||||||||
#include <sys/malloc.h> | #include <sys/malloc.h> | |||||||||
#include <sys/mount.h> | #include <sys/mount.h> | |||||||||
#include <sys/namei.h> | #include <sys/namei.h> | |||||||||
#include <sys/priv.h> | #include <sys/priv.h> | |||||||||
#include <sys/proc.h> | #include <sys/proc.h> | |||||||||
#include <sys/resourcevar.h> | ||||||||||
#include <sys/unistd.h> | #include <sys/unistd.h> | |||||||||
#include <sys/vnode.h> | #include <sys/vnode.h> | |||||||||
#include <sys/socket.h> | #include <sys/socket.h> | |||||||||
#include <sys/stat.h> | #include <sys/stat.h> | |||||||||
#include <sys/ktrace.h> | #include <sys/ktrace.h> | |||||||||
#include <sys/sx.h> | #include <sys/sx.h> | |||||||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | |||||||||
#include <sys/sysent.h> | #include <sys/sysent.h> | |||||||||
▲ Show 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | ||||||||||
u_int ktr_geniosize = PAGE_SIZE; | u_int ktr_geniosize = PAGE_SIZE; | |||||||||
SYSCTL_UINT(_kern_ktrace, OID_AUTO, genio_size, CTLFLAG_RWTUN, &ktr_geniosize, | SYSCTL_UINT(_kern_ktrace, OID_AUTO, genio_size, CTLFLAG_RWTUN, &ktr_geniosize, | |||||||||
0, "Maximum size of genio event payload"); | 0, "Maximum size of genio event payload"); | |||||||||
static int print_message = 1; | static int print_message = 1; | |||||||||
static struct mtx ktrace_mtx; | static struct mtx ktrace_mtx; | |||||||||
static struct sx ktrace_sx; | static struct sx ktrace_sx; | |||||||||
struct ktr_io_params { | ||||||||||
struct vnode *vp; | ||||||||||
struct ucred *cr; | ||||||||||
off_t lim; | ||||||||||
u_int refs; | ||||||||||
markjUnsubmitted Done Inline Actions
markj: | ||||||||||
}; | ||||||||||
static void ktrace_init(void *dummy); | static void ktrace_init(void *dummy); | |||||||||
static int sysctl_kern_ktrace_request_pool(SYSCTL_HANDLER_ARGS); | static int sysctl_kern_ktrace_request_pool(SYSCTL_HANDLER_ARGS); | |||||||||
static u_int ktrace_resize_pool(u_int oldsize, u_int newsize); | static u_int ktrace_resize_pool(u_int oldsize, u_int newsize); | |||||||||
static struct ktr_request *ktr_getrequest_entered(struct thread *td, int type); | static struct ktr_request *ktr_getrequest_entered(struct thread *td, int type); | |||||||||
static struct ktr_request *ktr_getrequest(int type); | static struct ktr_request *ktr_getrequest(int type); | |||||||||
static void ktr_submitrequest(struct thread *td, struct ktr_request *req); | static void ktr_submitrequest(struct thread *td, struct ktr_request *req); | |||||||||
static void ktr_freeproc(struct proc *p, struct ucred **uc, | static struct ktr_io_params *ktr_freeproc(struct proc *p); | |||||||||
struct vnode **vp); | ||||||||||
static void ktr_freerequest(struct ktr_request *req); | static void ktr_freerequest(struct ktr_request *req); | |||||||||
static void ktr_freerequest_locked(struct ktr_request *req); | static void ktr_freerequest_locked(struct ktr_request *req); | |||||||||
static void ktr_writerequest(struct thread *td, struct ktr_request *req); | static void ktr_writerequest(struct thread *td, struct ktr_request *req); | |||||||||
static int ktrcanset(struct thread *,struct proc *); | static int ktrcanset(struct thread *,struct proc *); | |||||||||
static int ktrsetchildren(struct thread *,struct proc *,int,int,struct vnode *); | static int ktrsetchildren(struct thread *, struct proc *, int, int, | |||||||||
static int ktrops(struct thread *,struct proc *,int,int,struct vnode *); | struct ktr_io_params *); | |||||||||
static int ktrops(struct thread *, struct proc *, int, int, | ||||||||||
struct ktr_io_params *); | ||||||||||
static void ktrprocctor_entered(struct thread *, struct proc *); | static void ktrprocctor_entered(struct thread *, struct proc *); | |||||||||
/* | /* | |||||||||
* ktrace itself generates events, such as context switches, which we do not | * ktrace itself generates events, such as context switches, which we do not | |||||||||
* wish to trace. Maintain a flag, TDP_INKTRACE, on each thread to determine | * wish to trace. Maintain a flag, TDP_INKTRACE, on each thread to determine | |||||||||
* whether or not it is in a region where tracing of events should be | * whether or not it is in a region where tracing of events should be | |||||||||
* suppressed. | * suppressed. | |||||||||
*/ | */ | |||||||||
▲ Show 20 Lines • Show All 246 Lines • ▼ Show 20 Lines | ||||||||||
{ | { | |||||||||
mtx_assert(&ktrace_mtx, MA_OWNED); | mtx_assert(&ktrace_mtx, MA_OWNED); | |||||||||
if (req->ktr_buffer != NULL) | if (req->ktr_buffer != NULL) | |||||||||
free(req->ktr_buffer, M_KTRACE); | free(req->ktr_buffer, M_KTRACE); | |||||||||
STAILQ_INSERT_HEAD(&ktr_free, req, ktr_list); | STAILQ_INSERT_HEAD(&ktr_free, req, ktr_list); | |||||||||
} | } | |||||||||
static void | ||||||||||
ktr_io_params_ref(struct ktr_io_params *kiop) | ||||||||||
{ | ||||||||||
mtx_assert(&ktrace_mtx, MA_OWNED); | ||||||||||
kiop->refs++; | ||||||||||
} | ||||||||||
static struct ktr_io_params * | ||||||||||
ktr_io_params_rele(struct ktr_io_params *kiop) | ||||||||||
Done Inline Actionsktr_io_params_rele would be a more standard name. markj: `ktr_io_params_rele` would be a more standard name. | ||||||||||
{ | ||||||||||
mtx_assert(&ktrace_mtx, MA_OWNED); | ||||||||||
if (kiop == NULL) | ||||||||||
return (NULL); | ||||||||||
KASSERT(kiop->refs > 0, ("kiop ref == 0 %p", kiop)); | ||||||||||
return (--(kiop->refs) == 0 ? kiop : NULL); | ||||||||||
} | ||||||||||
void | ||||||||||
ktr_io_params_free(struct ktr_io_params *kiop) | ||||||||||
{ | ||||||||||
if (kiop == NULL) | ||||||||||
return; | ||||||||||
MPASS(kiop->refs == 0); | ||||||||||
vrele(kiop->vp); | ||||||||||
crfree(kiop->cr); | ||||||||||
free(kiop, M_KTRACE); | ||||||||||
} | ||||||||||
static struct ktr_io_params * | ||||||||||
ktr_io_params_alloc(struct thread *td, struct vnode *vp) | ||||||||||
{ | ||||||||||
struct ktr_io_params *res; | ||||||||||
res = malloc(sizeof(struct ktr_io_params), M_KTRACE, M_WAITOK); | ||||||||||
res->vp = vp; | ||||||||||
vref(vp); | ||||||||||
Done Inline ActionsThese two lines can be res->cr = crhold(td->td_ucred). markj: These two lines can be `res->cr = crhold(td->td_ucred)`. | ||||||||||
res->cr = crhold(td->td_ucred); | ||||||||||
res->lim = lim_cur(td, RLIMIT_FSIZE); | ||||||||||
res->refs = 0; | ||||||||||
return (res); | ||||||||||
} | ||||||||||
/* | /* | |||||||||
* Disable tracing for a process and release all associated resources. | * Disable tracing for a process and release all associated resources. | |||||||||
* The caller is responsible for releasing a reference on the returned | * The caller is responsible for releasing a reference on the returned | |||||||||
* vnode and credentials. | * vnode and credentials. | |||||||||
*/ | */ | |||||||||
static void | static struct ktr_io_params * | |||||||||
ktr_freeproc(struct proc *p, struct ucred **uc, struct vnode **vp) | ktr_freeproc(struct proc *p) | |||||||||
{ | { | |||||||||
struct ktr_io_params *kiop; | ||||||||||
struct ktr_request *req; | struct ktr_request *req; | |||||||||
PROC_LOCK_ASSERT(p, MA_OWNED); | PROC_LOCK_ASSERT(p, MA_OWNED); | |||||||||
mtx_assert(&ktrace_mtx, MA_OWNED); | mtx_assert(&ktrace_mtx, MA_OWNED); | |||||||||
*uc = p->p_tracecred; | kiop = ktr_io_params_rele(p->p_ktrioparms); | |||||||||
p->p_tracecred = NULL; | p->p_ktrioparms = NULL; | |||||||||
Done Inline ActionsI think it is not guaranteed in all cases that p->p_ktrioparams is not NULL. For instance, ktrace(KTROP_CLEAR) on a process which did not have tracing enabled before. markj: I think it is not guaranteed in all cases that `p->p_ktrioparams` is not NULL. For instance… | ||||||||||
Done Inline ActionsYes, _unref/_rele should handle NULL arg, same as ktr_io_params_free(). I missed this. kib: Yes, _unref/_rele should handle NULL arg, same as ktr_io_params_free(). I missed this. | ||||||||||
if (vp != NULL) | ||||||||||
*vp = p->p_tracevp; | ||||||||||
p->p_tracevp = NULL; | ||||||||||
p->p_traceflag = 0; | p->p_traceflag = 0; | |||||||||
while ((req = STAILQ_FIRST(&p->p_ktr)) != NULL) { | while ((req = STAILQ_FIRST(&p->p_ktr)) != NULL) { | |||||||||
STAILQ_REMOVE_HEAD(&p->p_ktr, ktr_list); | STAILQ_REMOVE_HEAD(&p->p_ktr, ktr_list); | |||||||||
ktr_freerequest_locked(req); | ktr_freerequest_locked(req); | |||||||||
} | } | |||||||||
return (kiop); | ||||||||||
} | } | |||||||||
struct vnode * | ||||||||||
ktr_get_tracevp(struct proc *p, bool ref) | ||||||||||
{ | ||||||||||
struct vnode *vp; | ||||||||||
PROC_LOCK_ASSERT(p, MA_OWNED); | ||||||||||
if (p->p_ktrioparms != NULL) { | ||||||||||
Done Inline Actions
markj: | ||||||||||
vp = p->p_ktrioparms->vp; | ||||||||||
if (ref) | ||||||||||
vrefact(vp); | ||||||||||
} else { | ||||||||||
vp = NULL; | ||||||||||
} | ||||||||||
return (vp); | ||||||||||
} | ||||||||||
void | void | |||||||||
ktrsyscall(int code, int narg, register_t args[]) | ktrsyscall(int code, int narg, register_t args[]) | |||||||||
{ | { | |||||||||
struct ktr_request *req; | struct ktr_request *req; | |||||||||
struct ktr_syscall *ktp; | struct ktr_syscall *ktp; | |||||||||
size_t buflen; | size_t buflen; | |||||||||
char *buf = NULL; | char *buf = NULL; | |||||||||
Show All 40 Lines | ktrsysret(int code, int error, register_t retval) | |||||||||
ktr_submitrequest(curthread, req); | ktr_submitrequest(curthread, req); | |||||||||
} | } | |||||||||
/* | /* | |||||||||
* When a setuid process execs, disable tracing. | * When a setuid process execs, disable tracing. | |||||||||
* | * | |||||||||
* XXX: We toss any pending asynchronous records. | * XXX: We toss any pending asynchronous records. | |||||||||
*/ | */ | |||||||||
void | struct ktr_io_params * | |||||||||
ktrprocexec(struct proc *p, struct ucred **uc, struct vnode **vp) | ktrprocexec(struct proc *p) | |||||||||
{ | { | |||||||||
struct ktr_io_params *kiop; | ||||||||||
PROC_LOCK_ASSERT(p, MA_OWNED); | PROC_LOCK_ASSERT(p, MA_OWNED); | |||||||||
kiop = p->p_ktrioparms; | ||||||||||
if (kiop == NULL || priv_check_cred(kiop->cr, PRIV_DEBUG_DIFFCRED)) | ||||||||||
return (NULL); | ||||||||||
mtx_lock(&ktrace_mtx); | mtx_lock(&ktrace_mtx); | |||||||||
ktr_freeproc(p, uc, vp); | kiop = ktr_freeproc(p); | |||||||||
mtx_unlock(&ktrace_mtx); | mtx_unlock(&ktrace_mtx); | |||||||||
return (kiop); | ||||||||||
} | } | |||||||||
/* | /* | |||||||||
* When a process exits, drain per-process asynchronous trace records | * When a process exits, drain per-process asynchronous trace records | |||||||||
* and disable tracing. | * and disable tracing. | |||||||||
*/ | */ | |||||||||
void | void | |||||||||
ktrprocexit(struct thread *td) | ktrprocexit(struct thread *td) | |||||||||
{ | { | |||||||||
struct ktr_request *req; | struct ktr_request *req; | |||||||||
struct proc *p; | struct proc *p; | |||||||||
struct ucred *cred; | struct ktr_io_params *kiop; | |||||||||
struct vnode *vp; | ||||||||||
p = td->td_proc; | p = td->td_proc; | |||||||||
if (p->p_traceflag == 0) | if (p->p_traceflag == 0) | |||||||||
return; | return; | |||||||||
ktrace_enter(td); | ktrace_enter(td); | |||||||||
req = ktr_getrequest_entered(td, KTR_PROCDTOR); | req = ktr_getrequest_entered(td, KTR_PROCDTOR); | |||||||||
if (req != NULL) | if (req != NULL) | |||||||||
ktr_enqueuerequest(td, req); | ktr_enqueuerequest(td, req); | |||||||||
sx_xlock(&ktrace_sx); | sx_xlock(&ktrace_sx); | |||||||||
ktr_drain(td); | ktr_drain(td); | |||||||||
sx_xunlock(&ktrace_sx); | sx_xunlock(&ktrace_sx); | |||||||||
PROC_LOCK(p); | PROC_LOCK(p); | |||||||||
mtx_lock(&ktrace_mtx); | mtx_lock(&ktrace_mtx); | |||||||||
ktr_freeproc(p, &cred, &vp); | kiop = ktr_freeproc(p); | |||||||||
mtx_unlock(&ktrace_mtx); | mtx_unlock(&ktrace_mtx); | |||||||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | |||||||||
if (vp != NULL) | ktr_io_params_free(kiop); | |||||||||
vrele(vp); | ||||||||||
if (cred != NULL) | ||||||||||
crfree(cred); | ||||||||||
ktrace_exit(td); | ktrace_exit(td); | |||||||||
} | } | |||||||||
static void | static void | |||||||||
ktrprocctor_entered(struct thread *td, struct proc *p) | ktrprocctor_entered(struct thread *td, struct proc *p) | |||||||||
{ | { | |||||||||
struct ktr_proc_ctor *ktp; | struct ktr_proc_ctor *ktp; | |||||||||
struct ktr_request *req; | struct ktr_request *req; | |||||||||
Show All 24 Lines | ||||||||||
/* | /* | |||||||||
* When a process forks, enable tracing in the new process if needed. | * When a process forks, enable tracing in the new process if needed. | |||||||||
*/ | */ | |||||||||
void | void | |||||||||
ktrprocfork(struct proc *p1, struct proc *p2) | ktrprocfork(struct proc *p1, struct proc *p2) | |||||||||
{ | { | |||||||||
MPASS(p2->p_tracevp == NULL); | MPASS(p2->p_ktrioparms == NULL); | |||||||||
MPASS(p2->p_traceflag == 0); | MPASS(p2->p_traceflag == 0); | |||||||||
if (p1->p_traceflag == 0) | if (p1->p_traceflag == 0) | |||||||||
return; | return; | |||||||||
PROC_LOCK(p1); | PROC_LOCK(p1); | |||||||||
mtx_lock(&ktrace_mtx); | mtx_lock(&ktrace_mtx); | |||||||||
if (p1->p_traceflag & KTRFAC_INHERIT) { | if (p1->p_traceflag & KTRFAC_INHERIT) { | |||||||||
p2->p_traceflag = p1->p_traceflag; | p2->p_traceflag = p1->p_traceflag; | |||||||||
if ((p2->p_tracevp = p1->p_tracevp) != NULL) { | if ((p2->p_ktrioparms = p1->p_ktrioparms) != NULL) | |||||||||
VREF(p2->p_tracevp); | p1->p_ktrioparms->refs++; | |||||||||
KASSERT(p1->p_tracecred != NULL, | ||||||||||
("ktrace vnode with no cred")); | ||||||||||
p2->p_tracecred = crhold(p1->p_tracecred); | ||||||||||
} | } | |||||||||
} | ||||||||||
mtx_unlock(&ktrace_mtx); | mtx_unlock(&ktrace_mtx); | |||||||||
PROC_UNLOCK(p1); | PROC_UNLOCK(p1); | |||||||||
ktrprocctor(p2); | ktrprocctor(p2); | |||||||||
} | } | |||||||||
/* | /* | |||||||||
* When a thread returns, drain any asynchronous records generated by the | * When a thread returns, drain any asynchronous records generated by the | |||||||||
▲ Show 20 Lines • Show All 316 Lines • ▼ Show 20 Lines | #ifdef KTRACE | |||||||||
struct proc *p; | struct proc *p; | |||||||||
struct pgrp *pg; | struct pgrp *pg; | |||||||||
int facs = uap->facs & ~KTRFAC_ROOT; | int facs = uap->facs & ~KTRFAC_ROOT; | |||||||||
int ops = KTROP(uap->ops); | int ops = KTROP(uap->ops); | |||||||||
int descend = uap->ops & KTRFLAG_DESCEND; | int descend = uap->ops & KTRFLAG_DESCEND; | |||||||||
int nfound, ret = 0; | int nfound, ret = 0; | |||||||||
int flags, error = 0; | int flags, error = 0; | |||||||||
struct nameidata nd; | struct nameidata nd; | |||||||||
struct ucred *cred; | struct ktr_io_params *kiop, *old_kiop; | |||||||||
/* | /* | |||||||||
* Need something to (un)trace. | * Need something to (un)trace. | |||||||||
*/ | */ | |||||||||
if (ops != KTROP_CLEARFILE && facs == 0) | if (ops != KTROP_CLEARFILE && facs == 0) | |||||||||
return (EINVAL); | return (EINVAL); | |||||||||
kiop = NULL; | ||||||||||
ktrace_enter(td); | ktrace_enter(td); | |||||||||
if (ops != KTROP_CLEAR) { | if (ops != KTROP_CLEAR) { | |||||||||
/* | /* | |||||||||
* an operation which requires a file argument. | * an operation which requires a file argument. | |||||||||
*/ | */ | |||||||||
NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->fname, td); | NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->fname, td); | |||||||||
flags = FREAD | FWRITE | O_NOFOLLOW; | flags = FREAD | FWRITE | O_NOFOLLOW; | |||||||||
error = vn_open(&nd, &flags, 0, NULL); | error = vn_open(&nd, &flags, 0, NULL); | |||||||||
if (error) { | if (error) { | |||||||||
ktrace_exit(td); | ktrace_exit(td); | |||||||||
return (error); | return (error); | |||||||||
} | } | |||||||||
NDFREE(&nd, NDF_ONLY_PNBUF); | NDFREE(&nd, NDF_ONLY_PNBUF); | |||||||||
vp = nd.ni_vp; | vp = nd.ni_vp; | |||||||||
VOP_UNLOCK(vp); | VOP_UNLOCK(vp); | |||||||||
if (vp->v_type != VREG) { | if (vp->v_type != VREG) { | |||||||||
(void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td); | (void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td); | |||||||||
ktrace_exit(td); | ktrace_exit(td); | |||||||||
return (EACCES); | return (EACCES); | |||||||||
} | } | |||||||||
kiop = ktr_io_params_alloc(td, vp); | ||||||||||
Done Inline ActionsThis routine bumps the usecount on the vnode, but isn't it already incremented for us? markj: This routine bumps the usecount on the vnode, but isn't it already incremented for us? | ||||||||||
Done Inline ActionsIn fact the issue is that vn_close() was missed. kib: In fact the issue is that vn_close() was missed. | ||||||||||
kiop->refs++; | ||||||||||
} | } | |||||||||
/* | /* | |||||||||
* Clear all uses of the tracefile. | * Clear all uses of the tracefile. | |||||||||
*/ | */ | |||||||||
if (ops == KTROP_CLEARFILE) { | if (ops == KTROP_CLEARFILE) { | |||||||||
int vrele_count; | restart: | |||||||||
vrele_count = 0; | ||||||||||
sx_slock(&allproc_lock); | sx_slock(&allproc_lock); | |||||||||
FOREACH_PROC_IN_SYSTEM(p) { | FOREACH_PROC_IN_SYSTEM(p) { | |||||||||
old_kiop = NULL; | ||||||||||
PROC_LOCK(p); | PROC_LOCK(p); | |||||||||
if (p->p_tracevp == vp) { | if (p->p_ktrioparms != NULL && | |||||||||
p->p_ktrioparms->vp == vp) { | ||||||||||
Done Inline ActionsNeed to check p->p_ktrioparms != NULL first. markj: Need to check `p->p_ktrioparms != NULL` first. | ||||||||||
if (ktrcanset(td, p)) { | if (ktrcanset(td, p)) { | |||||||||
mtx_lock(&ktrace_mtx); | mtx_lock(&ktrace_mtx); | |||||||||
ktr_freeproc(p, &cred, NULL); | old_kiop = ktr_freeproc(p); | |||||||||
mtx_unlock(&ktrace_mtx); | mtx_unlock(&ktrace_mtx); | |||||||||
vrele_count++; | ||||||||||
crfree(cred); | ||||||||||
} else | } else | |||||||||
error = EPERM; | error = EPERM; | |||||||||
} | } | |||||||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | |||||||||
} | if (old_kiop != NULL) { | |||||||||
sx_sunlock(&allproc_lock); | sx_sunlock(&allproc_lock); | |||||||||
if (vrele_count > 0) { | ktr_io_params_free(old_kiop); | |||||||||
while (vrele_count-- > 0) | goto restart; | |||||||||
vrele(vp); | ||||||||||
} | } | |||||||||
} | ||||||||||
sx_sunlock(&allproc_lock); | ||||||||||
goto done; | goto done; | |||||||||
} | } | |||||||||
/* | /* | |||||||||
* do it | * do it | |||||||||
*/ | */ | |||||||||
sx_slock(&proctree_lock); | sx_slock(&proctree_lock); | |||||||||
if (uap->pid < 0) { | if (uap->pid < 0) { | |||||||||
/* | /* | |||||||||
Show All 15 Lines | LIST_FOREACH(p, &pg->pg_members, p_pglist) { | |||||||||
PROC_LOCK(p); | PROC_LOCK(p); | |||||||||
if (p->p_state == PRS_NEW || | if (p->p_state == PRS_NEW || | |||||||||
p_cansee(td, p) != 0) { | p_cansee(td, p) != 0) { | |||||||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | |||||||||
continue; | continue; | |||||||||
} | } | |||||||||
nfound++; | nfound++; | |||||||||
if (descend) | if (descend) | |||||||||
ret |= ktrsetchildren(td, p, ops, facs, vp); | ret |= ktrsetchildren(td, p, ops, facs, kiop); | |||||||||
else | else | |||||||||
ret |= ktrops(td, p, ops, facs, vp); | ret |= ktrops(td, p, ops, facs, kiop); | |||||||||
} | } | |||||||||
if (nfound == 0) { | if (nfound == 0) { | |||||||||
sx_sunlock(&proctree_lock); | sx_sunlock(&proctree_lock); | |||||||||
error = ESRCH; | error = ESRCH; | |||||||||
goto done; | goto done; | |||||||||
} | } | |||||||||
} else { | } else { | |||||||||
/* | /* | |||||||||
* by pid | * by pid | |||||||||
*/ | */ | |||||||||
p = pfind(uap->pid); | p = pfind(uap->pid); | |||||||||
if (p == NULL) | if (p == NULL) | |||||||||
error = ESRCH; | error = ESRCH; | |||||||||
else | else | |||||||||
error = p_cansee(td, p); | error = p_cansee(td, p); | |||||||||
if (error) { | if (error) { | |||||||||
if (p != NULL) | if (p != NULL) | |||||||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | |||||||||
sx_sunlock(&proctree_lock); | sx_sunlock(&proctree_lock); | |||||||||
goto done; | goto done; | |||||||||
} | } | |||||||||
if (descend) | if (descend) | |||||||||
ret |= ktrsetchildren(td, p, ops, facs, vp); | ret |= ktrsetchildren(td, p, ops, facs, kiop); | |||||||||
else | else | |||||||||
ret |= ktrops(td, p, ops, facs, vp); | ret |= ktrops(td, p, ops, facs, kiop); | |||||||||
} | } | |||||||||
sx_sunlock(&proctree_lock); | sx_sunlock(&proctree_lock); | |||||||||
if (!ret) | if (!ret) | |||||||||
error = EPERM; | error = EPERM; | |||||||||
done: | done: | |||||||||
if (vp != NULL) | if (kiop != NULL) { | |||||||||
(void) vn_close(vp, FWRITE, td->td_ucred, td); | mtx_lock(&ktrace_mtx); | |||||||||
kiop = ktr_io_params_rele(kiop); | ||||||||||
mtx_unlock(&ktrace_mtx); | ||||||||||
Done Inline ActionsShouldn't the mutex be released first? markj: Shouldn't the mutex be released first? | ||||||||||
ktr_io_params_free(kiop); | ||||||||||
} | ||||||||||
ktrace_exit(td); | ktrace_exit(td); | |||||||||
return (error); | return (error); | |||||||||
#else /* !KTRACE */ | #else /* !KTRACE */ | |||||||||
return (ENOSYS); | return (ENOSYS); | |||||||||
#endif /* KTRACE */ | #endif /* KTRACE */ | |||||||||
} | } | |||||||||
/* ARGSUSED */ | /* ARGSUSED */ | |||||||||
Show All 27 Lines | #ifdef KTRACE | |||||||||
return (0); | return (0); | |||||||||
#else /* !KTRACE */ | #else /* !KTRACE */ | |||||||||
return (ENOSYS); | return (ENOSYS); | |||||||||
#endif /* KTRACE */ | #endif /* KTRACE */ | |||||||||
} | } | |||||||||
#ifdef KTRACE | #ifdef KTRACE | |||||||||
static int | static int | |||||||||
ktrops(struct thread *td, struct proc *p, int ops, int facs, struct vnode *vp) | ktrops(struct thread *td, struct proc *p, int ops, int facs, | |||||||||
struct ktr_io_params *new_kiop) | ||||||||||
{ | { | |||||||||
struct vnode *tracevp = NULL; | struct ktr_io_params *old_kiop; | |||||||||
struct ucred *tracecred = NULL; | ||||||||||
PROC_LOCK_ASSERT(p, MA_OWNED); | PROC_LOCK_ASSERT(p, MA_OWNED); | |||||||||
if (!ktrcanset(td, p)) { | if (!ktrcanset(td, p)) { | |||||||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | |||||||||
return (0); | return (0); | |||||||||
} | } | |||||||||
if (p->p_flag & P_WEXIT) { | if (p->p_flag & P_WEXIT) { | |||||||||
/* If the process is exiting, just ignore it. */ | /* If the process is exiting, just ignore it. */ | |||||||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | |||||||||
return (1); | return (1); | |||||||||
} | } | |||||||||
old_kiop = NULL; | ||||||||||
mtx_lock(&ktrace_mtx); | mtx_lock(&ktrace_mtx); | |||||||||
if (ops == KTROP_SET) { | if (ops == KTROP_SET) { | |||||||||
if (p->p_tracevp != vp) { | if (p->p_ktrioparms != NULL && | |||||||||
/* | p->p_ktrioparms->vp != new_kiop->vp) { | |||||||||
* if trace file already in use, relinquish below | /* if trace file already in use, relinquish below */ | |||||||||
*/ | old_kiop = ktr_io_params_rele(p->p_ktrioparms); | |||||||||
tracevp = p->p_tracevp; | p->p_ktrioparms = NULL; | |||||||||
VREF(vp); | ||||||||||
p->p_tracevp = vp; | ||||||||||
} | } | |||||||||
if (p->p_tracecred != td->td_ucred) { | if (p->p_ktrioparms == NULL) { | |||||||||
tracecred = p->p_tracecred; | p->p_ktrioparms = new_kiop; | |||||||||
p->p_tracecred = crhold(td->td_ucred); | ktr_io_params_ref(new_kiop); | |||||||||
} | } | |||||||||
p->p_traceflag |= facs; | p->p_traceflag |= facs; | |||||||||
if (priv_check(td, PRIV_KTRACE) == 0) | if (priv_check(td, PRIV_KTRACE) == 0) | |||||||||
p->p_traceflag |= KTRFAC_ROOT; | p->p_traceflag |= KTRFAC_ROOT; | |||||||||
} else { | } else { | |||||||||
/* KTROP_CLEAR */ | /* KTROP_CLEAR */ | |||||||||
if (((p->p_traceflag &= ~facs) & KTRFAC_MASK) == 0) | if (((p->p_traceflag &= ~facs) & KTRFAC_MASK) == 0) | |||||||||
/* no more tracing */ | /* no more tracing */ | |||||||||
ktr_freeproc(p, &tracecred, &tracevp); | old_kiop = ktr_freeproc(p); | |||||||||
} | } | |||||||||
mtx_unlock(&ktrace_mtx); | mtx_unlock(&ktrace_mtx); | |||||||||
if ((p->p_traceflag & KTRFAC_MASK) != 0) | if ((p->p_traceflag & KTRFAC_MASK) != 0) | |||||||||
ktrprocctor_entered(td, p); | ktrprocctor_entered(td, p); | |||||||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | |||||||||
if (tracevp != NULL) | ktr_io_params_free(old_kiop); | |||||||||
vrele(tracevp); | ||||||||||
if (tracecred != NULL) | ||||||||||
crfree(tracecred); | ||||||||||
return (1); | return (1); | |||||||||
} | } | |||||||||
static int | static int | |||||||||
ktrsetchildren(struct thread *td, struct proc *top, int ops, int facs, | ktrsetchildren(struct thread *td, struct proc *top, int ops, int facs, | |||||||||
struct vnode *vp) | struct ktr_io_params *new_kiop) | |||||||||
{ | { | |||||||||
struct proc *p; | struct proc *p; | |||||||||
int ret = 0; | int ret = 0; | |||||||||
p = top; | p = top; | |||||||||
PROC_LOCK_ASSERT(p, MA_OWNED); | PROC_LOCK_ASSERT(p, MA_OWNED); | |||||||||
sx_assert(&proctree_lock, SX_LOCKED); | sx_assert(&proctree_lock, SX_LOCKED); | |||||||||
for (;;) { | for (;;) { | |||||||||
ret |= ktrops(td, p, ops, facs, vp); | ret |= ktrops(td, p, ops, facs, new_kiop); | |||||||||
/* | /* | |||||||||
* If this process has children, descend to them next, | * If this process has children, descend to them next, | |||||||||
* otherwise do any siblings, and if done with this level, | * otherwise do any siblings, and if done with this level, | |||||||||
* follow back up the tree (but not past top). | * follow back up the tree (but not past top). | |||||||||
*/ | */ | |||||||||
if (!LIST_EMPTY(&p->p_children)) | if (!LIST_EMPTY(&p->p_children)) | |||||||||
p = LIST_FIRST(&p->p_children); | p = LIST_FIRST(&p->p_children); | |||||||||
else for (;;) { | else for (;;) { | |||||||||
if (p == top) | if (p == top) | |||||||||
return (ret); | return (ret); | |||||||||
if (LIST_NEXT(p, p_sibling)) { | if (LIST_NEXT(p, p_sibling)) { | |||||||||
p = LIST_NEXT(p, p_sibling); | p = LIST_NEXT(p, p_sibling); | |||||||||
break; | break; | |||||||||
} | } | |||||||||
p = p->p_pptr; | p = p->p_pptr; | |||||||||
} | } | |||||||||
PROC_LOCK(p); | PROC_LOCK(p); | |||||||||
} | } | |||||||||
/*NOTREACHED*/ | /*NOTREACHED*/ | |||||||||
} | } | |||||||||
static void | static void | |||||||||
ktr_writerequest(struct thread *td, struct ktr_request *req) | ktr_writerequest(struct thread *td, struct ktr_request *req) | |||||||||
{ | { | |||||||||
struct ktr_io_params *kiop; | ||||||||||
struct ktr_header *kth; | struct ktr_header *kth; | |||||||||
struct vnode *vp; | struct vnode *vp; | |||||||||
struct proc *p; | struct proc *p; | |||||||||
struct ucred *cred; | struct ucred *cred; | |||||||||
struct uio auio; | struct uio auio; | |||||||||
struct iovec aiov[3]; | struct iovec aiov[3]; | |||||||||
struct mount *mp; | struct mount *mp; | |||||||||
int datalen, buflen, vrele_count; | off_t lim; | |||||||||
int datalen, buflen; | ||||||||||
int error; | int error; | |||||||||
p = td->td_proc; | ||||||||||
/* | /* | |||||||||
* We hold the vnode and credential for use in I/O in case ktrace is | * We hold the vnode and credential for use in I/O in case ktrace is | |||||||||
* disabled on the process as we write out the request. | * disabled on the process as we write out the request. | |||||||||
* | * | |||||||||
* XXXRW: This is not ideal: we could end up performing a write after | * XXXRW: This is not ideal: we could end up performing a write after | |||||||||
* the vnode has been closed. | * the vnode has been closed. | |||||||||
*/ | */ | |||||||||
mtx_lock(&ktrace_mtx); | mtx_lock(&ktrace_mtx); | |||||||||
vp = td->td_proc->p_tracevp; | ||||||||||
cred = td->td_proc->p_tracecred; | ||||||||||
kiop = p->p_ktrioparms; | ||||||||||
/* | /* | |||||||||
* If vp is NULL, the vp has been cleared out from under this | * If kiop is NULL, it has been cleared out from under this | |||||||||
* request, so just drop it. Make sure the credential and vnode are | * request, so just drop it. | |||||||||
* in sync: we should have both or neither. | ||||||||||
*/ | */ | |||||||||
if (vp == NULL) { | if (kiop == NULL) { | |||||||||
KASSERT(cred == NULL, ("ktr_writerequest: cred != NULL")); | ||||||||||
mtx_unlock(&ktrace_mtx); | mtx_unlock(&ktrace_mtx); | |||||||||
return; | return; | |||||||||
} | } | |||||||||
vp = kiop->vp; | ||||||||||
cred = kiop->cr; | ||||||||||
lim = kiop->lim; | ||||||||||
VREF(vp); | VREF(vp); | |||||||||
KASSERT(cred != NULL, ("ktr_writerequest: cred == NULL")); | KASSERT(cred != NULL, ("ktr_writerequest: cred == NULL")); | |||||||||
crhold(cred); | crhold(cred); | |||||||||
mtx_unlock(&ktrace_mtx); | mtx_unlock(&ktrace_mtx); | |||||||||
kth = &req->ktr_header; | kth = &req->ktr_header; | |||||||||
KASSERT(((u_short)kth->ktr_type & ~KTR_DROP) < nitems(data_lengths), | KASSERT(((u_short)kth->ktr_type & ~KTR_DROP) < nitems(data_lengths), | |||||||||
("data_lengths array overflow")); | ("data_lengths array overflow")); | |||||||||
Show All 20 Lines | if (buflen != 0) { | |||||||||
aiov[auio.uio_iovcnt].iov_base = req->ktr_buffer; | aiov[auio.uio_iovcnt].iov_base = req->ktr_buffer; | |||||||||
aiov[auio.uio_iovcnt].iov_len = buflen; | aiov[auio.uio_iovcnt].iov_len = buflen; | |||||||||
auio.uio_resid += buflen; | auio.uio_resid += buflen; | |||||||||
auio.uio_iovcnt++; | auio.uio_iovcnt++; | |||||||||
} | } | |||||||||
vn_start_write(vp, &mp, V_WAIT); | vn_start_write(vp, &mp, V_WAIT); | |||||||||
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); | vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); | |||||||||
MPASS((td->td_pflags2 & TDP2_KTRWRITE) == 0); | ||||||||||
td->td_pflags2 |= TDP2_KTRWRITE; | ||||||||||
Done Inline ActionsActually I wonder if TDP2_KTRWRITE is really needed, it is guaranteed that TDP_INKTRACE is set here if the write is coming from ktr_writerequest(). markj: Actually I wonder if TDP2_KTRWRITE is really needed, it is guaranteed that TDP_INKTRACE is set… | ||||||||||
Done Inline ActionsI think, concern there would be for any operation done under ktrace_enter() and resulting in fs write. It seems that it is not possible to do this right now, from code inspection. kib: I think, concern there would be for any operation done under ktrace_enter() and resulting in fs… | ||||||||||
td->td_ktr_io_lim = lim; | ||||||||||
#ifdef MAC | #ifdef MAC | |||||||||
error = mac_vnode_check_write(cred, NOCRED, vp); | error = mac_vnode_check_write(cred, NOCRED, vp); | |||||||||
if (error == 0) | if (error == 0) | |||||||||
#endif | #endif | |||||||||
error = VOP_WRITE(vp, &auio, IO_UNIT | IO_APPEND, cred); | error = VOP_WRITE(vp, &auio, IO_UNIT | IO_APPEND, cred); | |||||||||
td->td_pflags2 &= ~TDP2_KTRWRITE; | ||||||||||
VOP_UNLOCK(vp); | VOP_UNLOCK(vp); | |||||||||
vn_finished_write(mp); | vn_finished_write(mp); | |||||||||
crfree(cred); | crfree(cred); | |||||||||
if (!error) { | if (error == 0) { | |||||||||
vrele(vp); | vrele(vp); | |||||||||
return; | return; | |||||||||
} | } | |||||||||
/* | /* | |||||||||
* If error encountered, give up tracing on this vnode. We defer | * If error encountered, give up tracing on this vnode on this | |||||||||
* all the vrele()'s on the vnode until after we are finished walking | * process. Other processes might still be suitable for | |||||||||
* the various lists to avoid needlessly holding locks. | * writes to this vnode. | |||||||||
* NB: at this point we still hold the vnode reference that must | ||||||||||
* not go away as we need the valid vnode to compare with. Thus let | ||||||||||
* vrele_count start at 1 and the reference will be freed | ||||||||||
* by the loop at the end after our last use of vp. | ||||||||||
*/ | */ | |||||||||
log(LOG_NOTICE, "ktrace write failed, errno %d, tracing stopped\n", | log(LOG_NOTICE, | |||||||||
error); | "ktrace write failed, errno %d, tracing stopped for pid %d\n", | |||||||||
vrele_count = 1; | error, p->p_pid); | |||||||||
/* | ||||||||||
* First, clear this vnode from being used by any processes in the | ||||||||||
* system. | ||||||||||
* XXX - If one process gets an EPERM writing to the vnode, should | ||||||||||
* we really do this? Other processes might have suitable | ||||||||||
* credentials for the operation. | ||||||||||
*/ | ||||||||||
cred = NULL; | ||||||||||
sx_slock(&allproc_lock); | ||||||||||
FOREACH_PROC_IN_SYSTEM(p) { | ||||||||||
PROC_LOCK(p); | PROC_LOCK(p); | |||||||||
if (p->p_tracevp == vp) { | ||||||||||
mtx_lock(&ktrace_mtx); | mtx_lock(&ktrace_mtx); | |||||||||
ktr_freeproc(p, &cred, NULL); | if (p->p_ktrioparms != NULL && p->p_ktrioparms->vp == vp) | |||||||||
kiop = ktr_freeproc(p); | ||||||||||
mtx_unlock(&ktrace_mtx); | mtx_unlock(&ktrace_mtx); | |||||||||
vrele_count++; | ||||||||||
} | ||||||||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | |||||||||
if (cred != NULL) { | ktr_io_params_free(kiop); | |||||||||
crfree(cred); | ||||||||||
cred = NULL; | ||||||||||
} | ||||||||||
} | ||||||||||
sx_sunlock(&allproc_lock); | ||||||||||
while (vrele_count-- > 0) | ||||||||||
vrele(vp); | vrele(vp); | |||||||||
} | } | |||||||||
/* | /* | |||||||||
* Return true if caller has permission to set the ktracing state | * Return true if caller has permission to set the ktracing state | |||||||||
* of target. Essentially, the target can't possess any | * of target. Essentially, the target can't possess any | |||||||||
* more permissions than the caller. KTRFAC_ROOT signifies that | * more permissions than the caller. KTRFAC_ROOT signifies that | |||||||||
* root previously set the tracing status on the target process, and | * root previously set the tracing status on the target process, and | |||||||||
* so, only root may further change it. | * so, only root may further change it. | |||||||||
Show All 17 Lines |