123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660 |
- /* $OpenBSD: kern_ktrace.c,v 1.75 2015/08/01 20:12:34 guenther Exp $ */
- /* $NetBSD: kern_ktrace.c,v 1.23 1996/02/09 18:59:36 christos Exp $ */
- /*
- * Copyright (c) 1989, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * @(#)kern_ktrace.c 8.2 (Berkeley) 9/23/93
- */
- #include <sys/param.h>
- #include <sys/systm.h>
- #include <sys/proc.h>
- #include <sys/sched.h>
- #include <sys/file.h>
- #include <sys/namei.h>
- #include <sys/vnode.h>
- #include <sys/lock.h>
- #include <sys/ktrace.h>
- #include <sys/malloc.h>
- #include <sys/syslog.h>
- #include <sys/sysctl.h>
- #include <sys/mount.h>
- #include <sys/syscall.h>
- #include <sys/syscallargs.h>
- #include <uvm/uvm_extern.h>
- void ktrinitheaderraw(struct ktr_header *, uint, pid_t, pid_t);
- void ktrinitheader(struct ktr_header *, struct proc *, int);
- void ktrstart(struct proc *, struct vnode *, struct ucred *);
- void ktremulraw(struct proc *, struct process *, pid_t);
- int ktrops(struct proc *, struct process *, int, int, struct vnode *,
- struct ucred *);
- int ktrsetchildren(struct proc *, struct process *, int, int,
- struct vnode *, struct ucred *);
- int ktrwrite(struct proc *, struct ktr_header *, const void *, size_t);
- int ktrwrite2(struct proc *, struct ktr_header *, const void *, size_t,
- const void *, size_t);
- int ktrwriteraw(struct proc *, struct vnode *, struct ucred *,
- struct ktr_header *, struct iovec *);
- int ktrcanset(struct proc *, struct process *);
- /*
- * Clear the trace settings in a correct way (to avoid races).
- */
- void
- ktrcleartrace(struct process *pr)
- {
- struct vnode *vp;
- struct ucred *cred;
- if (pr->ps_tracevp != NULL) {
- vp = pr->ps_tracevp;
- cred = pr->ps_tracecred;
- pr->ps_traceflag = 0;
- pr->ps_tracevp = NULL;
- pr->ps_tracecred = NULL;
- vrele(vp);
- crfree(cred);
- }
- }
- /*
- * Change the trace setting in a correct way (to avoid races).
- */
- void
- ktrsettrace(struct process *pr, int facs, struct vnode *newvp,
- struct ucred *newcred)
- {
- struct vnode *oldvp;
- struct ucred *oldcred;
- KASSERT(newvp != NULL);
- KASSERT(newcred != NULL);
- pr->ps_traceflag |= facs;
- /* nothing to change about where the trace goes? */
- if (pr->ps_tracevp == newvp && pr->ps_tracecred == newcred)
- return;
- vref(newvp);
- crhold(newcred);
- oldvp = pr->ps_tracevp;
- oldcred = pr->ps_tracecred;
- pr->ps_tracevp = newvp;
- pr->ps_tracecred = newcred;
- if (oldvp != NULL) {
- vrele(oldvp);
- crfree(oldcred);
- }
- }
- void
- ktrinitheaderraw(struct ktr_header *kth, uint type, pid_t pid, pid_t tid)
- {
- memset(kth, 0, sizeof(struct ktr_header));
- kth->ktr_type = type;
- nanotime(&kth->ktr_time);
- kth->ktr_pid = pid;
- kth->ktr_tid = tid;
- }
- void
- ktrinitheader(struct ktr_header *kth, struct proc *p, int type)
- {
- ktrinitheaderraw(kth, type, p->p_p->ps_pid,
- p->p_pid + THREAD_PID_OFFSET);
- memcpy(kth->ktr_comm, p->p_comm, MAXCOMLEN);
- }
- void
- ktrstart(struct proc *p, struct vnode *vp, struct ucred *cred)
- {
- struct ktr_header kth;
- ktrinitheaderraw(&kth, htobe32(KTR_START), -1, -1);
- ktrwriteraw(p, vp, cred, &kth, NULL);
- }
- void
- ktrsyscall(struct proc *p, register_t code, size_t argsize, register_t args[])
- {
- struct ktr_header kth;
- struct ktr_syscall *ktp;
- size_t len = sizeof(struct ktr_syscall) + argsize;
- register_t *argp;
- u_int nargs = 0;
- int i;
- if (code == SYS___sysctl && (p->p_p->ps_emul->e_flags & EMUL_NATIVE)) {
- /*
- * The native sysctl encoding stores the mib[]
- * array because it is interesting.
- */
- if (args[1] > 0)
- nargs = lmin(args[1], CTL_MAXNAME);
- len += nargs * sizeof(int);
- }
- atomic_setbits_int(&p->p_flag, P_INKTR);
- ktrinitheader(&kth, p, KTR_SYSCALL);
- ktp = malloc(len, M_TEMP, M_WAITOK);
- ktp->ktr_code = code;
- ktp->ktr_argsize = argsize;
- argp = (register_t *)((char *)ktp + sizeof(struct ktr_syscall));
- for (i = 0; i < (argsize / sizeof *argp); i++)
- *argp++ = args[i];
- if (nargs && copyin((void *)args[0], argp, nargs * sizeof(int)))
- memset(argp, 0, nargs * sizeof(int));
- ktrwrite(p, &kth, ktp, len);
- free(ktp, M_TEMP, len);
- atomic_clearbits_int(&p->p_flag, P_INKTR);
- }
- void
- ktrsysret(struct proc *p, register_t code, int error,
- const register_t retval[2])
- {
- struct ktr_header kth;
- struct ktr_sysret ktp;
- int len;
- atomic_setbits_int(&p->p_flag, P_INKTR);
- ktrinitheader(&kth, p, KTR_SYSRET);
- ktp.ktr_code = code;
- ktp.ktr_error = error;
- if (error)
- len = 0;
- else if (code == SYS_lseek)
- /* the one exception: lseek on ILP32 needs more */
- len = sizeof(long long);
- else
- len = sizeof(register_t);
- ktrwrite2(p, &kth, &ktp, sizeof(ktp), retval, len);
- atomic_clearbits_int(&p->p_flag, P_INKTR);
- }
- void
- ktrnamei(struct proc *p, char *path)
- {
- struct ktr_header kth;
- atomic_setbits_int(&p->p_flag, P_INKTR);
- ktrinitheader(&kth, p, KTR_NAMEI);
- ktrwrite(p, &kth, path, strlen(path));
- atomic_clearbits_int(&p->p_flag, P_INKTR);
- }
- void
- ktremulraw(struct proc *curp, struct process *pr, pid_t tid)
- {
- struct ktr_header kth;
- char *emul = pr->ps_emul->e_name;
- struct iovec data[2];
- ktrinitheaderraw(&kth, KTR_EMUL, pr->ps_pid, tid);
- data[0].iov_base = emul;
- data[0].iov_len = strlen(emul);
- data[1].iov_len = 0;
- kth.ktr_len = data[0].iov_len;
- ktrwriteraw(curp, pr->ps_tracevp, pr->ps_tracecred, &kth, data);
- }
- void
- ktremul(struct proc *p)
- {
- atomic_setbits_int(&p->p_flag, P_INKTR);
- ktremulraw(p, p->p_p, p->p_pid + THREAD_PID_OFFSET);
- atomic_clearbits_int(&p->p_flag, P_INKTR);
- }
- void
- ktrgenio(struct proc *p, int fd, enum uio_rw rw, struct iovec *iov,
- ssize_t len)
- {
- struct ktr_header kth;
- struct ktr_genio ktp;
- caddr_t cp;
- int count;
- int buflen;
- atomic_setbits_int(&p->p_flag, P_INKTR);
- /* beware overflow */
- if (len > PAGE_SIZE)
- buflen = PAGE_SIZE;
- else
- buflen = len + sizeof(struct ktr_genio);
- ktrinitheader(&kth, p, KTR_GENIO);
- ktp.ktr_fd = fd;
- ktp.ktr_rw = rw;
- cp = malloc(buflen, M_TEMP, M_WAITOK);
- while (len > 0) {
- /*
- * Don't allow this process to hog the cpu when doing
- * huge I/O.
- */
- if (curcpu()->ci_schedstate.spc_schedflags & SPCF_SHOULDYIELD)
- preempt(NULL);
- count = lmin(iov->iov_len, buflen);
- if (count > len)
- count = len;
- if (copyin(iov->iov_base, cp, count))
- break;
- if (ktrwrite2(p, &kth, &ktp, sizeof(ktp), cp, count) != 0)
- break;
- iov->iov_len -= count;
- iov->iov_base = (caddr_t)iov->iov_base + count;
- if (iov->iov_len == 0)
- iov++;
- len -= count;
- }
- free(cp, M_TEMP, buflen);
- atomic_clearbits_int(&p->p_flag, P_INKTR);
- }
- void
- ktrpsig(struct proc *p, int sig, sig_t action, int mask, int code,
- siginfo_t *si)
- {
- struct ktr_header kth;
- struct ktr_psig kp;
- atomic_setbits_int(&p->p_flag, P_INKTR);
- ktrinitheader(&kth, p, KTR_PSIG);
- kp.signo = (char)sig;
- kp.action = action;
- kp.mask = mask;
- kp.code = code;
- kp.si = *si;
- ktrwrite(p, &kth, &kp, sizeof(kp));
- atomic_clearbits_int(&p->p_flag, P_INKTR);
- }
- void
- ktrcsw(struct proc *p, int out, int user)
- {
- struct ktr_header kth;
- struct ktr_csw kc;
- atomic_setbits_int(&p->p_flag, P_INKTR);
- ktrinitheader(&kth, p, KTR_CSW);
- kc.out = out;
- kc.user = user;
- ktrwrite(p, &kth, &kc, sizeof(kc));
- atomic_clearbits_int(&p->p_flag, P_INKTR);
- }
- void
- ktrstruct(struct proc *p, const char *name, const void *data, size_t datalen)
- {
- struct ktr_header kth;
- KERNEL_ASSERT_LOCKED();
- atomic_setbits_int(&p->p_flag, P_INKTR);
- ktrinitheader(&kth, p, KTR_STRUCT);
-
- if (data == NULL)
- datalen = 0;
- ktrwrite2(p, &kth, name, strlen(name) + 1, data, datalen);
- atomic_clearbits_int(&p->p_flag, P_INKTR);
- }
- int
- ktruser(struct proc *p, const char *id, const void *addr, size_t len)
- {
- struct ktr_header kth;
- struct ktr_user ktp;
- int error;
- void *memp;
- #define STK_PARAMS 128
- long long stkbuf[STK_PARAMS / sizeof(long long)];
- if (!KTRPOINT(p, KTR_USER))
- return (0);
- if (len > KTR_USER_MAXLEN)
- return (EINVAL);
- atomic_setbits_int(&p->p_flag, P_INKTR);
- ktrinitheader(&kth, p, KTR_USER);
- memset(ktp.ktr_id, 0, KTR_USER_MAXIDLEN);
- error = copyinstr(id, ktp.ktr_id, KTR_USER_MAXIDLEN, NULL);
- if (error == 0) {
- if (len > sizeof(stkbuf))
- memp = malloc(len, M_TEMP, M_WAITOK);
- else
- memp = stkbuf;
- error = copyin(addr, memp, len);
- if (error == 0)
- ktrwrite2(p, &kth, &ktp, sizeof(ktp), memp, len);
- if (memp != stkbuf)
- free(memp, M_TEMP, len);
- }
- atomic_clearbits_int(&p->p_flag, P_INKTR);
- return (error);
- }
- /* Interface and common routines */
- /*
- * ktrace system call
- */
- /* ARGSUSED */
- int
- sys_ktrace(struct proc *curp, void *v, register_t *retval)
- {
- struct sys_ktrace_args /* {
- syscallarg(const char *) fname;
- syscallarg(int) ops;
- syscallarg(int) facs;
- syscallarg(pid_t) pid;
- } */ *uap = v;
- struct vnode *vp = NULL;
- struct process *pr = NULL;
- struct ucred *cred = NULL;
- struct pgrp *pg;
- int facs = SCARG(uap, facs) & ~((unsigned) KTRFAC_ROOT);
- int ops = KTROP(SCARG(uap, ops));
- int descend = SCARG(uap, ops) & KTRFLAG_DESCEND;
- int ret = 0;
- int error = 0;
- struct nameidata nd;
- if (ops != KTROP_CLEAR) {
- /*
- * an operation which requires a file argument.
- */
- cred = curp->p_ucred;
- NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, fname),
- curp);
- if ((error = vn_open(&nd, FREAD|FWRITE|O_NOFOLLOW, 0)) != 0)
- goto done;
- vp = nd.ni_vp;
- VOP_UNLOCK(vp, 0, curp);
- if (vp->v_type != VREG) {
- error = EACCES;
- goto done;
- }
- }
- /*
- * Clear all uses of the tracefile
- */
- if (ops == KTROP_CLEARFILE) {
- LIST_FOREACH(pr, &allprocess, ps_list) {
- if (pr->ps_tracevp == vp) {
- if (ktrcanset(curp, pr))
- ktrcleartrace(pr);
- else
- error = EPERM;
- }
- }
- goto done;
- }
- /*
- * need something to (un)trace (XXX - why is this here?)
- */
- if (!facs) {
- error = EINVAL;
- goto done;
- }
- if (ops == KTROP_SET) {
- if (suser(curp, 0) == 0)
- facs |= KTRFAC_ROOT;
- ktrstart(curp, vp, cred);
- }
- /*
- * do it
- */
- if (SCARG(uap, pid) < 0) {
- /*
- * by process group
- */
- pg = pgfind(-SCARG(uap, pid));
- if (pg == NULL) {
- error = ESRCH;
- goto done;
- }
- LIST_FOREACH(pr, &pg->pg_members, ps_pglist) {
- if (descend)
- ret |= ktrsetchildren(curp, pr, ops, facs, vp,
- cred);
- else
- ret |= ktrops(curp, pr, ops, facs, vp, cred);
- }
- } else {
- /*
- * by pid
- */
- pr = prfind(SCARG(uap, pid));
- if (pr == NULL) {
- error = ESRCH;
- goto done;
- }
- if (descend)
- ret |= ktrsetchildren(curp, pr, ops, facs, vp, cred);
- else
- ret |= ktrops(curp, pr, ops, facs, vp, cred);
- }
- if (!ret)
- error = EPERM;
- done:
- if (vp != NULL)
- (void) vn_close(vp, FREAD|FWRITE, cred, curp);
- return (error);
- }
- int
- ktrops(struct proc *curp, struct process *pr, int ops, int facs,
- struct vnode *vp, struct ucred *cred)
- {
- if (!ktrcanset(curp, pr))
- return (0);
- if (ops == KTROP_SET)
- ktrsettrace(pr, facs, vp, cred);
- else {
- /* KTROP_CLEAR */
- pr->ps_traceflag &= ~facs;
- if ((pr->ps_traceflag & KTRFAC_MASK) == 0) {
- /* cleared all the facility bits, so stop completely */
- ktrcleartrace(pr);
- }
- }
- /*
- * Emit an emulation record every time there is a ktrace
- * change/attach request.
- */
- if (pr->ps_traceflag & KTRFAC_EMUL)
- ktremulraw(curp, pr, -1);
- return (1);
- }
- int
- ktrsetchildren(struct proc *curp, struct process *top, int ops, int facs,
- struct vnode *vp, struct ucred *cred)
- {
- struct process *pr;
- int ret = 0;
- pr = top;
- for (;;) {
- ret |= ktrops(curp, pr, ops, facs, vp, cred);
- /*
- * If this process has children, descend to them next,
- * otherwise do any siblings, and if done with this level,
- * follow back up the tree (but not past top).
- */
- if (!LIST_EMPTY(&pr->ps_children))
- pr = LIST_FIRST(&pr->ps_children);
- else for (;;) {
- if (pr == top)
- return (ret);
- if (LIST_NEXT(pr, ps_sibling) != NULL) {
- pr = LIST_NEXT(pr, ps_sibling);
- break;
- }
- pr = pr->ps_pptr;
- }
- }
- /*NOTREACHED*/
- }
- int
- ktrwrite(struct proc *p, struct ktr_header *kth, const void *aux, size_t len)
- {
- struct vnode *vp = p->p_p->ps_tracevp;
- struct ucred *cred = p->p_p->ps_tracecred;
- struct iovec data[2];
- int error;
- if (vp == NULL)
- return 0;
- crhold(cred);
- data[0].iov_base = (void *)aux;
- data[0].iov_len = len;
- data[1].iov_len = 0;
- kth->ktr_len = len;
- error = ktrwriteraw(p, vp, cred, kth, data);
- crfree(cred);
- return (error);
- }
- int
- ktrwrite2(struct proc *p, struct ktr_header *kth, const void *aux1,
- size_t len1, const void *aux2, size_t len2)
- {
- struct vnode *vp = p->p_p->ps_tracevp;
- struct ucred *cred = p->p_p->ps_tracecred;
- struct iovec data[2];
- int error;
- if (vp == NULL)
- return 0;
- crhold(cred);
- data[0].iov_base = (void *)aux1;
- data[0].iov_len = len1;
- data[1].iov_base = (void *)aux2;
- data[1].iov_len = len2;
- kth->ktr_len = len1 + len2;
- error = ktrwriteraw(p, vp, cred, kth, data);
- crfree(cred);
- return (error);
- }
- int
- ktrwriteraw(struct proc *curp, struct vnode *vp, struct ucred *cred,
- struct ktr_header *kth, struct iovec *data)
- {
- struct uio auio;
- struct iovec aiov[3];
- struct process *pr;
- int error;
- auio.uio_iov = &aiov[0];
- auio.uio_offset = 0;
- auio.uio_segflg = UIO_SYSSPACE;
- auio.uio_rw = UIO_WRITE;
- aiov[0].iov_base = (caddr_t)kth;
- aiov[0].iov_len = sizeof(struct ktr_header);
- auio.uio_resid = sizeof(struct ktr_header);
- auio.uio_iovcnt = 1;
- auio.uio_procp = curp;
- if (kth->ktr_len > 0) {
- aiov[1] = data[0];
- aiov[2] = data[1];
- auio.uio_iovcnt++;
- if (aiov[2].iov_len > 0)
- auio.uio_iovcnt++;
- auio.uio_resid += kth->ktr_len;
- }
- vget(vp, LK_EXCLUSIVE | LK_RETRY, curp);
- error = VOP_WRITE(vp, &auio, IO_UNIT|IO_APPEND, cred);
- if (!error) {
- vput(vp);
- return (0);
- }
- /*
- * If error encountered, give up tracing on this vnode.
- */
- log(LOG_NOTICE, "ktrace write failed, errno %d, tracing stopped\n",
- error);
- LIST_FOREACH(pr, &allprocess, ps_list)
- if (pr->ps_tracevp == vp && pr->ps_tracecred == cred)
- ktrcleartrace(pr);
- vput(vp);
- return (error);
- }
- /*
- * Return true if caller has permission to set the ktracing state
- * of target. Essentially, the target can't possess any
- * more permissions than the caller. KTRFAC_ROOT signifies that
- * root previously set the tracing status on the target process, and
- * so, only root may further change it.
- *
- * TODO: check groups. use caller effective gid.
- */
- int
- ktrcanset(struct proc *callp, struct process *targetpr)
- {
- struct ucred *caller = callp->p_ucred;
- struct ucred *target = targetpr->ps_ucred;
- if ((caller->cr_uid == target->cr_ruid &&
- target->cr_ruid == target->cr_svuid &&
- caller->cr_rgid == target->cr_rgid && /* XXX */
- target->cr_rgid == target->cr_svgid &&
- (targetpr->ps_traceflag & KTRFAC_ROOT) == 0 &&
- !ISSET(targetpr->ps_flags, PS_SUGID)) ||
- caller->cr_uid == 0)
- return (1);
- return (0);
- }
|