root/kern/kern_ktrace.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ktrsettracevnode
  2. ktrinitheader
  3. ktrsyscall
  4. ktrsysret
  5. ktrnamei
  6. ktremul
  7. ktrgenio
  8. ktrpsig
  9. ktrcsw
  10. sys_ktrace
  11. ktrops
  12. ktrsetchildren
  13. ktrwrite
  14. ktrcanset

    1 /*      $OpenBSD: kern_ktrace.c,v 1.42 2007/05/16 17:27:30 art Exp $    */
    2 /*      $NetBSD: kern_ktrace.c,v 1.23 1996/02/09 18:59:36 christos Exp $        */
    3 
    4 /*
    5  * Copyright (c) 1989, 1993
    6  *      The Regents of the University of California.  All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  * 3. Neither the name of the University nor the names of its contributors
   17  *    may be used to endorse or promote products derived from this software
   18  *    without specific prior written permission.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   30  * SUCH DAMAGE.
   31  *
   32  *      @(#)kern_ktrace.c       8.2 (Berkeley) 9/23/93
   33  */
   34 
   35 #ifdef KTRACE
   36 
   37 #include <sys/param.h>
   38 #include <sys/systm.h>
   39 #include <sys/proc.h>
   40 #include <sys/sched.h>
   41 #include <sys/file.h>
   42 #include <sys/namei.h>
   43 #include <sys/vnode.h>
   44 #include <sys/ktrace.h>
   45 #include <sys/malloc.h>
   46 #include <sys/syslog.h>
   47 #include <sys/sysctl.h>
   48 
   49 #include <sys/mount.h>
   50 #include <sys/syscall.h>
   51 #include <sys/syscallargs.h>
   52 
   53 #include <uvm/uvm_extern.h>
   54 
   55 void ktrinitheader(struct ktr_header *, struct proc *, int);
   56 int ktrops(struct proc *, struct proc *, int, int, struct vnode *);
   57 int ktrsetchildren(struct proc *, struct proc *, int, int,
   58                         struct vnode *);
   59 int ktrwrite(struct proc *, struct ktr_header *);
   60 int ktrcanset(struct proc *, struct proc *);
   61 
   62 /*
   63  * Change the trace vnode in a correct way (to avoid races).
   64  */
   65 void
   66 ktrsettracevnode(struct proc *p, struct vnode *newvp)
   67 {
   68         struct vnode *vp;
   69 
   70         if (p->p_tracep == newvp)       /* avoid work */
   71                 return;
   72 
   73         if (newvp != NULL)
   74                 VREF(newvp);
   75 
   76         vp = p->p_tracep;
   77         p->p_tracep = newvp;
   78 
   79         if (vp != NULL)
   80                 vrele(vp);
   81 }
   82 
   83 void
   84 ktrinitheader(struct ktr_header *kth, struct proc *p, int type)
   85 {
   86         bzero(kth, sizeof (struct ktr_header));
   87         kth->ktr_type = type;
   88         microtime(&kth->ktr_time);
   89         kth->ktr_pid = p->p_pid;
   90         bcopy(p->p_comm, kth->ktr_comm, MAXCOMLEN);     
   91 }
   92 
   93 void
   94 ktrsyscall(struct proc *p, register_t code, size_t argsize, register_t args[])
   95 {
   96         struct  ktr_header kth;
   97         struct  ktr_syscall *ktp;
   98         size_t len = sizeof(struct ktr_syscall) + argsize;
   99         register_t *argp;
  100         u_int nargs = 0;
  101         int i;
  102 
  103         if (code == SYS___sysctl && (p->p_emul->e_flags & EMUL_NATIVE)) {
  104                 /*
  105                  * The native sysctl encoding stores the mib[]
  106                  * array because it is interesting.
  107                  */
  108                 if (args[1] > 0)
  109                         nargs = min(args[1], CTL_MAXNAME);
  110                 len += nargs * sizeof(int);
  111         }
  112         p->p_traceflag |= KTRFAC_ACTIVE;
  113         ktrinitheader(&kth, p, KTR_SYSCALL);
  114         ktp = malloc(len, M_TEMP, M_WAITOK);
  115         ktp->ktr_code = code;
  116         ktp->ktr_argsize = argsize;
  117         argp = (register_t *)((char *)ktp + sizeof(struct ktr_syscall));
  118         for (i = 0; i < (argsize / sizeof *argp); i++)
  119                 *argp++ = args[i];
  120         if (code == SYS___sysctl && (p->p_emul->e_flags & EMUL_NATIVE) &&
  121             nargs &&
  122             copyin((void *)args[0], argp, nargs * sizeof(int)))
  123                 bzero(argp, nargs * sizeof(int));
  124         kth.ktr_buf = (caddr_t)ktp;
  125         kth.ktr_len = len;
  126         ktrwrite(p, &kth);
  127         free(ktp, M_TEMP);
  128         p->p_traceflag &= ~KTRFAC_ACTIVE;
  129 }
  130 
  131 void
  132 ktrsysret(struct proc *p, register_t code, int error, register_t retval)
  133 {
  134         struct ktr_header kth;
  135         struct ktr_sysret ktp;
  136 
  137         p->p_traceflag |= KTRFAC_ACTIVE;
  138         ktrinitheader(&kth, p, KTR_SYSRET);
  139         ktp.ktr_code = code;
  140         ktp.ktr_error = error;
  141         ktp.ktr_retval = retval;                /* what about val2 ? */
  142 
  143         kth.ktr_buf = (caddr_t)&ktp;
  144         kth.ktr_len = sizeof(struct ktr_sysret);
  145 
  146         ktrwrite(p, &kth);
  147         p->p_traceflag &= ~KTRFAC_ACTIVE;
  148 }
  149 
  150 void
  151 ktrnamei(struct proc *p, char *path)
  152 {
  153         struct ktr_header kth;
  154 
  155         p->p_traceflag |= KTRFAC_ACTIVE;
  156         ktrinitheader(&kth, p, KTR_NAMEI);
  157         kth.ktr_len = strlen(path);
  158         kth.ktr_buf = path;
  159 
  160         ktrwrite(p, &kth);
  161         p->p_traceflag &= ~KTRFAC_ACTIVE;
  162 }
  163 
  164 void
  165 ktremul(struct proc *p, char *emul)
  166 {
  167         struct ktr_header kth;
  168 
  169         p->p_traceflag |= KTRFAC_ACTIVE;
  170         ktrinitheader(&kth, p, KTR_EMUL);
  171         kth.ktr_len = strlen(emul);
  172         kth.ktr_buf = emul;
  173 
  174         ktrwrite(p, &kth);
  175         p->p_traceflag &= ~KTRFAC_ACTIVE;
  176 }
  177 
  178 void
  179 ktrgenio(struct proc *p, int fd, enum uio_rw rw, struct iovec *iov, int len,
  180     int error)
  181 {
  182         struct ktr_header kth;
  183         struct ktr_genio *ktp;
  184         caddr_t cp;
  185         int resid = len, count;
  186         int buflen;
  187 
  188         if (error)
  189                 return;
  190 
  191         p->p_traceflag |= KTRFAC_ACTIVE;
  192 
  193         buflen = min(PAGE_SIZE, len + sizeof(struct ktr_genio));
  194 
  195         ktrinitheader(&kth, p, KTR_GENIO);
  196         ktp = malloc(buflen, M_TEMP, M_WAITOK);
  197         ktp->ktr_fd = fd;
  198         ktp->ktr_rw = rw;
  199 
  200         kth.ktr_buf = (caddr_t)ktp;
  201 
  202         cp = (caddr_t)((char *)ktp + sizeof (struct ktr_genio));
  203         buflen -= sizeof(struct ktr_genio);
  204 
  205         while (resid > 0) {
  206                 /*
  207                  * Don't allow this process to hog the cpu when doing
  208                  * huge I/O.
  209                  */
  210                 if (curcpu()->ci_schedstate.spc_schedflags & SPCF_SHOULDYIELD)
  211                         preempt(NULL);
  212 
  213                 count = min(iov->iov_len, buflen);
  214                 if (count > resid)
  215                         count = resid;
  216                 if (copyin(iov->iov_base, cp, count))
  217                         break;
  218 
  219                 kth.ktr_len = count + sizeof(struct ktr_genio);
  220 
  221                 if (ktrwrite(p, &kth) != 0)
  222                         break;
  223 
  224                 iov->iov_len -= count;
  225                 iov->iov_base = (caddr_t)iov->iov_base + count;
  226 
  227                 if (iov->iov_len == 0)
  228                         iov++;
  229 
  230                 resid -= count;
  231         }
  232 
  233         free(ktp, M_TEMP);
  234         p->p_traceflag &= ~KTRFAC_ACTIVE;
  235         
  236 }
  237 
  238 void
  239 ktrpsig(struct proc *p, int sig, sig_t action, int mask, int code,
  240     siginfo_t *si)
  241 {
  242         struct ktr_header kth;
  243         struct ktr_psig kp;
  244 
  245         p->p_traceflag |= KTRFAC_ACTIVE;
  246         ktrinitheader(&kth, p, KTR_PSIG);
  247         kp.signo = (char)sig;
  248         kp.action = action;
  249         kp.mask = mask;
  250         kp.code = code;
  251         kp.si = *si;
  252         kth.ktr_buf = (caddr_t)&kp;
  253         kth.ktr_len = sizeof(struct ktr_psig);
  254 
  255         ktrwrite(p, &kth);
  256         p->p_traceflag &= ~KTRFAC_ACTIVE;
  257 }
  258 
  259 void
  260 ktrcsw(struct proc *p, int out, int user)
  261 {
  262         struct ktr_header kth;
  263         struct  ktr_csw kc;
  264 
  265         p->p_traceflag |= KTRFAC_ACTIVE;
  266         ktrinitheader(&kth, p, KTR_CSW);
  267         kc.out = out;
  268         kc.user = user;
  269         kth.ktr_buf = (caddr_t)&kc;
  270         kth.ktr_len = sizeof(struct ktr_csw);
  271 
  272         ktrwrite(p, &kth);
  273         p->p_traceflag &= ~KTRFAC_ACTIVE;
  274 }
  275 
  276 /* Interface and common routines */
  277 
  278 /*
  279  * ktrace system call
  280  */
  281 /* ARGSUSED */
  282 int
  283 sys_ktrace(struct proc *curp, void *v, register_t *retval)
  284 {
  285         struct sys_ktrace_args /* {
  286                 syscallarg(const char *) fname;
  287                 syscallarg(int) ops;
  288                 syscallarg(int) facs;
  289                 syscallarg(pid_t) pid;
  290         } */ *uap = v;
  291         struct vnode *vp = NULL;
  292         struct proc *p = NULL;
  293         struct pgrp *pg;
  294         int facs = SCARG(uap, facs) & ~((unsigned) KTRFAC_ROOT);
  295         int ops = KTROP(SCARG(uap, ops));
  296         int descend = SCARG(uap, ops) & KTRFLAG_DESCEND;
  297         int ret = 0;
  298         int error = 0;
  299         struct nameidata nd;
  300 
  301         curp->p_traceflag |= KTRFAC_ACTIVE;
  302         if (ops != KTROP_CLEAR) {
  303                 /*
  304                  * an operation which requires a file argument.
  305                  */
  306                 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, fname),
  307                     curp);
  308                 if ((error = vn_open(&nd, FREAD|FWRITE|O_NOFOLLOW, 0)) != 0) {
  309                         curp->p_traceflag &= ~KTRFAC_ACTIVE;
  310                         return (error);
  311                 }
  312                 vp = nd.ni_vp;
  313 
  314                 VOP_UNLOCK(vp, 0, curp);
  315                 if (vp->v_type != VREG) {
  316                         (void) vn_close(vp, FREAD|FWRITE, curp->p_ucred, curp);
  317                         curp->p_traceflag &= ~KTRFAC_ACTIVE;
  318                         return (EACCES);
  319                 }
  320         }
  321         /*
  322          * Clear all uses of the tracefile
  323          */
  324         if (ops == KTROP_CLEARFILE) {
  325                 for (p = LIST_FIRST(&allproc); p; p = LIST_NEXT(p, p_list)) {
  326                         if (p->p_tracep == vp) {
  327                                 if (ktrcanset(curp, p)) {
  328                                         p->p_traceflag = 0;
  329                                         ktrsettracevnode(p, NULL);
  330                                 } else
  331                                         error = EPERM;
  332                         }
  333                 }
  334                 goto done;
  335         }
  336         /*
  337          * need something to (un)trace (XXX - why is this here?)
  338          */
  339         if (!facs) {
  340                 error = EINVAL;
  341                 goto done;
  342         }
  343         /* 
  344          * do it
  345          */
  346         if (SCARG(uap, pid) < 0) {
  347                 /*
  348                  * by process group
  349                  */
  350                 pg = pgfind(-SCARG(uap, pid));
  351                 if (pg == NULL) {
  352                         error = ESRCH;
  353                         goto done;
  354                 }
  355                 LIST_FOREACH(p, &pg->pg_members, p_pglist)
  356                         if (descend)
  357                                 ret |= ktrsetchildren(curp, p, ops, facs, vp);
  358                         else 
  359                                 ret |= ktrops(curp, p, ops, facs, vp);
  360                                         
  361         } else {
  362                 /*
  363                  * by pid
  364                  */
  365                 p = pfind(SCARG(uap, pid));
  366                 if (p == NULL) {
  367                         error = ESRCH;
  368                         goto done;
  369                 }
  370                 if (descend)
  371                         ret |= ktrsetchildren(curp, p, ops, facs, vp);
  372                 else
  373                         ret |= ktrops(curp, p, ops, facs, vp);
  374         }
  375         if (!ret)
  376                 error = EPERM;
  377 done:
  378         if (vp != NULL)
  379                 (void) vn_close(vp, FWRITE, curp->p_ucred, curp);
  380         curp->p_traceflag &= ~KTRFAC_ACTIVE;
  381         return (error);
  382 }
  383 
  384 int
  385 ktrops(struct proc *curp, struct proc *p, int ops, int facs, struct vnode *vp)
  386 {
  387 
  388         if (!ktrcanset(curp, p))
  389                 return (0);
  390         if (ops == KTROP_SET) {
  391                 ktrsettracevnode(p, vp);
  392                 p->p_traceflag |= facs;
  393                 if (curp->p_ucred->cr_uid == 0)
  394                         p->p_traceflag |= KTRFAC_ROOT;
  395         } else {        
  396                 /* KTROP_CLEAR */
  397                 if (((p->p_traceflag &= ~facs) & KTRFAC_MASK) == 0) {
  398                         /* no more tracing */
  399                         p->p_traceflag = 0;
  400                         ktrsettracevnode(p, NULL);
  401                 }
  402         }
  403 
  404         /*
  405          * Emit an emulation record, every time there is a ktrace
  406          * change/attach request. 
  407          */
  408         if (KTRPOINT(p, KTR_EMUL))
  409                 ktremul(p, p->p_emul->e_name);
  410 
  411         return (1);
  412 }
  413 
  414 int
  415 ktrsetchildren(struct proc *curp, struct proc *top, int ops, int facs,
  416     struct vnode *vp)
  417 {
  418         struct proc *p;
  419         int ret = 0;
  420 
  421         p = top;
  422         for (;;) {
  423                 ret |= ktrops(curp, p, ops, facs, vp);
  424                 /*
  425                  * If this process has children, descend to them next,
  426                  * otherwise do any siblings, and if done with this level,
  427                  * follow back up the tree (but not past top).
  428                  */
  429                 if (!LIST_EMPTY(&p->p_children))
  430                         p = LIST_FIRST(&p->p_children);
  431                 else for (;;) {
  432                         if (p == top)
  433                                 return (ret);
  434                         if (LIST_NEXT(p, p_sibling) != NULL) {
  435                                 p = LIST_NEXT(p, p_sibling);
  436                                 break;
  437                         }
  438                         p = p->p_pptr;
  439                 }
  440         }
  441         /*NOTREACHED*/
  442 }
  443 
  444 int
  445 ktrwrite(struct proc *p, struct ktr_header *kth)
  446 {
  447         struct uio auio;
  448         struct iovec aiov[2];
  449         int error;
  450         struct vnode *vp = p->p_tracep;
  451 
  452         if (vp == NULL)
  453                 return 0;
  454         auio.uio_iov = &aiov[0];
  455         auio.uio_offset = 0;
  456         auio.uio_segflg = UIO_SYSSPACE;
  457         auio.uio_rw = UIO_WRITE;
  458         aiov[0].iov_base = (caddr_t)kth;
  459         aiov[0].iov_len = sizeof(struct ktr_header);
  460         auio.uio_resid = sizeof(struct ktr_header);
  461         auio.uio_iovcnt = 1;
  462         auio.uio_procp = p;
  463         if (kth->ktr_len > 0) {
  464                 auio.uio_iovcnt++;
  465                 aiov[1].iov_base = kth->ktr_buf;
  466                 aiov[1].iov_len = kth->ktr_len;
  467                 auio.uio_resid += kth->ktr_len;
  468         }
  469         vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
  470         error = VOP_WRITE(vp, &auio, IO_UNIT|IO_APPEND, p->p_ucred);
  471         VOP_UNLOCK(vp, 0, p);
  472         if (!error)
  473                 return 0;
  474         /*
  475          * If error encountered, give up tracing on this vnode.
  476          */
  477         log(LOG_NOTICE, "ktrace write failed, errno %d, tracing stopped\n",
  478             error);
  479         for (p = LIST_FIRST(&allproc); p != NULL; p = LIST_NEXT(p, p_list)) {
  480                 if (p->p_tracep == vp) {
  481                         p->p_traceflag = 0;
  482                         ktrsettracevnode(p, NULL);
  483                 }
  484         }
  485 
  486         return error;
  487 }
  488 
  489 /*
  490  * Return true if caller has permission to set the ktracing state
  491  * of target.  Essentially, the target can't possess any
  492  * more permissions than the caller.  KTRFAC_ROOT signifies that
  493  * root previously set the tracing status on the target process, and 
  494  * so, only root may further change it.
  495  *
  496  * TODO: check groups.  use caller effective gid.
  497  */
  498 int
  499 ktrcanset(struct proc *callp, struct proc *targetp)
  500 {
  501         struct pcred *caller = callp->p_cred;
  502         struct pcred *target = targetp->p_cred;
  503 
  504         if ((caller->pc_ucred->cr_uid == target->p_ruid &&
  505             target->p_ruid == target->p_svuid &&
  506             caller->p_rgid == target->p_rgid && /* XXX */
  507             target->p_rgid == target->p_svgid &&
  508             (targetp->p_traceflag & KTRFAC_ROOT) == 0 &&
  509             !ISSET(targetp->p_flag, P_SUGID)) ||
  510             caller->pc_ucred->cr_uid == 0)
  511                 return (1);
  512 
  513         return (0);
  514 }
  515 
  516 #endif

/* [<][>][^][v][top][bottom][index][help] */