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