Annotation of sys/kern/sys_process.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: sys_process.c,v 1.39 2007/04/10 17:47:55 miod Exp $ */
! 2: /* $NetBSD: sys_process.c,v 1.55 1996/05/15 06:17:47 tls Exp $ */
! 3:
! 4: /*-
! 5: * Copyright (c) 1994 Christopher G. Demetriou. All rights reserved.
! 6: * Copyright (c) 1982, 1986, 1989, 1993
! 7: * The Regents of the University of California. All rights reserved.
! 8: * (c) UNIX System Laboratories, Inc.
! 9: * All or some portions of this file are derived from material licensed
! 10: * to the University of California by American Telephone and Telegraph
! 11: * Co. or Unix System Laboratories, Inc. and are reproduced herein with
! 12: * the permission of UNIX System Laboratories, Inc.
! 13: *
! 14: * Redistribution and use in source and binary forms, with or without
! 15: * modification, are permitted provided that the following conditions
! 16: * are met:
! 17: * 1. Redistributions of source code must retain the above copyright
! 18: * notice, this list of conditions and the following disclaimer.
! 19: * 2. Redistributions in binary form must reproduce the above copyright
! 20: * notice, this list of conditions and the following disclaimer in the
! 21: * documentation and/or other materials provided with the distribution.
! 22: * 3. Neither the name of the University nor the names of its contributors
! 23: * may be used to endorse or promote products derived from this software
! 24: * without specific prior written permission.
! 25: *
! 26: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 27: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 28: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 29: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 30: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 31: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 32: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 33: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 34: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 35: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 36: * SUCH DAMAGE.
! 37: *
! 38: * from: @(#)sys_process.c 8.1 (Berkeley) 6/10/93
! 39: */
! 40:
! 41: /*
! 42: * References:
! 43: * (1) Bach's "The Design of the UNIX Operating System",
! 44: * (2) sys/miscfs/procfs from UCB's 4.4BSD-Lite distribution,
! 45: * (3) the "4.4BSD Programmer's Reference Manual" published
! 46: * by USENIX and O'Reilly & Associates.
! 47: * The 4.4BSD PRM does a reasonably good job of documenting what the various
! 48: * ptrace() requests should actually do, and its text is quoted several times
! 49: * in this file.
! 50: */
! 51:
! 52: #include <sys/param.h>
! 53: #include <sys/systm.h>
! 54: #include <sys/proc.h>
! 55: #include <sys/signalvar.h>
! 56: #include <sys/errno.h>
! 57: #include <sys/malloc.h>
! 58: #include <sys/ptrace.h>
! 59: #include <sys/uio.h>
! 60: #include <sys/user.h>
! 61: #include <sys/sched.h>
! 62:
! 63: #include <sys/mount.h>
! 64: #include <sys/syscallargs.h>
! 65:
! 66: #include <uvm/uvm_extern.h>
! 67:
! 68: #include <machine/reg.h>
! 69:
! 70: #ifdef PTRACE
! 71: /*
! 72: * Process debugging system call.
! 73: */
! 74: int
! 75: sys_ptrace(struct proc *p, void *v, register_t *retval)
! 76: {
! 77: struct sys_ptrace_args /* {
! 78: syscallarg(int) req;
! 79: syscallarg(pid_t) pid;
! 80: syscallarg(caddr_t) addr;
! 81: syscallarg(int) data;
! 82: } */ *uap = v;
! 83: struct proc *t; /* target process */
! 84: struct uio uio;
! 85: struct iovec iov;
! 86: struct ptrace_io_desc piod;
! 87: struct ptrace_event pe;
! 88: struct reg *regs;
! 89: #if defined (PT_SETFPREGS) || defined (PT_GETFPREGS)
! 90: struct fpreg *fpregs;
! 91: #endif
! 92: #if defined (PT_SETXMMREGS) || defined (PT_GETXMMREGS)
! 93: struct xmmregs *xmmregs;
! 94: #endif
! 95: #ifdef PT_WCOOKIE
! 96: register_t wcookie;
! 97: #endif
! 98: int error, write;
! 99: int temp;
! 100: int req;
! 101: int s;
! 102:
! 103: /* "A foolish consistency..." XXX */
! 104: if (SCARG(uap, req) == PT_TRACE_ME)
! 105: t = p;
! 106: else {
! 107:
! 108: /* Find the process we're supposed to be operating on. */
! 109: if ((t = pfind(SCARG(uap, pid))) == NULL)
! 110: return (ESRCH);
! 111: }
! 112:
! 113: if ((t->p_flag & P_INEXEC) != 0)
! 114: return (EAGAIN);
! 115:
! 116: /* Make sure we can operate on it. */
! 117: switch (SCARG(uap, req)) {
! 118: case PT_TRACE_ME:
! 119: /* Saying that you're being traced is always legal. */
! 120: break;
! 121:
! 122: case PT_ATTACH:
! 123: /*
! 124: * You can't attach to a process if:
! 125: * (1) it's the process that's doing the attaching,
! 126: */
! 127: if (t->p_pid == p->p_pid)
! 128: return (EINVAL);
! 129:
! 130: /*
! 131: * (2) it's a system process
! 132: */
! 133: if (ISSET(t->p_flag, P_SYSTEM))
! 134: return (EPERM);
! 135:
! 136: /*
! 137: * (3) it's already being traced, or
! 138: */
! 139: if (ISSET(t->p_flag, P_TRACED))
! 140: return (EBUSY);
! 141:
! 142: /*
! 143: * (4) it's not owned by you, or the last exec
! 144: * gave us setuid/setgid privs (unless
! 145: * you're root), or...
! 146: *
! 147: * [Note: once P_SUGID or P_SUGIDEXEC gets set in
! 148: * execve(), they stay set until the process does
! 149: * another execve(). Hence this prevents a setuid
! 150: * process which revokes its special privileges using
! 151: * setuid() from being traced. This is good security.]
! 152: */
! 153: if ((t->p_cred->p_ruid != p->p_cred->p_ruid ||
! 154: ISSET(t->p_flag, P_SUGIDEXEC) ||
! 155: ISSET(t->p_flag, P_SUGID)) &&
! 156: (error = suser(p, 0)) != 0)
! 157: return (error);
! 158:
! 159: /*
! 160: * (5) ...it's init, which controls the security level
! 161: * of the entire system, and the system was not
! 162: * compiled with permanently insecure mode turned
! 163: * on.
! 164: */
! 165: if ((t->p_pid == 1) && (securelevel > -1))
! 166: return (EPERM);
! 167: break;
! 168:
! 169: case PT_READ_I:
! 170: case PT_READ_D:
! 171: case PT_WRITE_I:
! 172: case PT_WRITE_D:
! 173: case PT_IO:
! 174: case PT_CONTINUE:
! 175: case PT_KILL:
! 176: case PT_DETACH:
! 177: #ifdef PT_STEP
! 178: case PT_STEP:
! 179: #endif
! 180: case PT_SET_EVENT_MASK:
! 181: case PT_GET_EVENT_MASK:
! 182: case PT_GET_PROCESS_STATE:
! 183: case PT_GETREGS:
! 184: case PT_SETREGS:
! 185: #ifdef PT_GETFPREGS
! 186: case PT_GETFPREGS:
! 187: #endif
! 188: #ifdef PT_SETFPREGS
! 189: case PT_SETFPREGS:
! 190: #endif
! 191: #ifdef PT_GETXMMREGS
! 192: case PT_GETXMMREGS:
! 193: #endif
! 194: #ifdef PT_SETXMMREGS
! 195: case PT_SETXMMREGS:
! 196: #endif
! 197: #ifdef PT_WCOOKIE
! 198: case PT_WCOOKIE:
! 199: #endif
! 200: /*
! 201: * You can't do what you want to the process if:
! 202: * (1) It's not being traced at all,
! 203: */
! 204: if (!ISSET(t->p_flag, P_TRACED))
! 205: return (EPERM);
! 206:
! 207: /*
! 208: * (2) it's not being traced by _you_, or
! 209: */
! 210: if (t->p_pptr != p)
! 211: return (EBUSY);
! 212:
! 213: /*
! 214: * (3) it's not currently stopped.
! 215: */
! 216: if (t->p_stat != SSTOP || !ISSET(t->p_flag, P_WAITED))
! 217: return (EBUSY);
! 218: break;
! 219:
! 220: default: /* It was not a legal request. */
! 221: return (EINVAL);
! 222: }
! 223:
! 224: /* Do single-step fixup if needed. */
! 225: FIX_SSTEP(t);
! 226:
! 227: /* Now do the operation. */
! 228: write = 0;
! 229: *retval = 0;
! 230:
! 231: switch (SCARG(uap, req)) {
! 232: case PT_TRACE_ME:
! 233: /* Just set the trace flag. */
! 234: atomic_setbits_int(&t->p_flag, P_TRACED);
! 235: t->p_oppid = t->p_pptr->p_pid;
! 236: if (t->p_ptstat == NULL)
! 237: t->p_ptstat = malloc(sizeof(*t->p_ptstat),
! 238: M_SUBPROC, M_WAITOK);
! 239: bzero(t->p_ptstat, sizeof(*t->p_ptstat));
! 240: return (0);
! 241:
! 242: case PT_WRITE_I: /* XXX no separate I and D spaces */
! 243: case PT_WRITE_D:
! 244: write = 1;
! 245: temp = SCARG(uap, data);
! 246: case PT_READ_I: /* XXX no separate I and D spaces */
! 247: case PT_READ_D:
! 248: /* write = 0 done above. */
! 249: iov.iov_base = (caddr_t)&temp;
! 250: iov.iov_len = sizeof(int);
! 251: uio.uio_iov = &iov;
! 252: uio.uio_iovcnt = 1;
! 253: uio.uio_offset = (off_t)(long)SCARG(uap, addr);
! 254: uio.uio_resid = sizeof(int);
! 255: uio.uio_segflg = UIO_SYSSPACE;
! 256: uio.uio_rw = write ? UIO_WRITE : UIO_READ;
! 257: uio.uio_procp = p;
! 258: error = process_domem(p, t, &uio, write ? PT_WRITE_I :
! 259: PT_READ_I);
! 260: if (write == 0)
! 261: *retval = temp;
! 262: return (error);
! 263: case PT_IO:
! 264: error = copyin(SCARG(uap, addr), &piod, sizeof(piod));
! 265: if (error)
! 266: return (error);
! 267: iov.iov_base = piod.piod_addr;
! 268: iov.iov_len = piod.piod_len;
! 269: uio.uio_iov = &iov;
! 270: uio.uio_iovcnt = 1;
! 271: uio.uio_offset = (off_t)(long)piod.piod_offs;
! 272: uio.uio_resid = piod.piod_len;
! 273: uio.uio_segflg = UIO_USERSPACE;
! 274: uio.uio_procp = p;
! 275: switch (piod.piod_op) {
! 276: case PIOD_READ_I:
! 277: req = PT_READ_I;
! 278: uio.uio_rw = UIO_READ;
! 279: break;
! 280: case PIOD_READ_D:
! 281: req = PT_READ_D;
! 282: uio.uio_rw = UIO_READ;
! 283: break;
! 284: case PIOD_WRITE_I:
! 285: req = PT_WRITE_I;
! 286: uio.uio_rw = UIO_WRITE;
! 287: break;
! 288: case PIOD_WRITE_D:
! 289: req = PT_WRITE_D;
! 290: uio.uio_rw = UIO_WRITE;
! 291: break;
! 292: default:
! 293: return (EINVAL);
! 294: }
! 295: error = process_domem(p, t, &uio, req);
! 296: piod.piod_len -= uio.uio_resid;
! 297: (void) copyout(&piod, SCARG(uap, addr), sizeof(piod));
! 298: return (error);
! 299: #ifdef PT_STEP
! 300: case PT_STEP:
! 301: /*
! 302: * From the 4.4BSD PRM:
! 303: * "Execution continues as in request PT_CONTINUE; however
! 304: * as soon as possible after execution of at least one
! 305: * instruction, execution stops again. [ ... ]"
! 306: */
! 307: #endif
! 308: case PT_CONTINUE:
! 309: /*
! 310: * From the 4.4BSD PRM:
! 311: * "The data argument is taken as a signal number and the
! 312: * child's execution continues at location addr as if it
! 313: * incurred that signal. Normally the signal number will
! 314: * be either 0 to indicate that the signal that caused the
! 315: * stop should be ignored, or that value fetched out of
! 316: * the process's image indicating which signal caused
! 317: * the stop. If addr is (int *)1 then execution continues
! 318: * from where it stopped."
! 319: */
! 320:
! 321: /* Check that the data is a valid signal number or zero. */
! 322: if (SCARG(uap, data) < 0 || SCARG(uap, data) >= NSIG)
! 323: return (EINVAL);
! 324:
! 325: /* If the address parameter is not (int *)1, set the pc. */
! 326: if ((int *)SCARG(uap, addr) != (int *)1)
! 327: if ((error = process_set_pc(t, SCARG(uap, addr))) != 0)
! 328: goto relebad;
! 329:
! 330: #ifdef PT_STEP
! 331: /*
! 332: * Arrange for a single-step, if that's requested and possible.
! 333: */
! 334: error = process_sstep(t, SCARG(uap, req) == PT_STEP);
! 335: if (error)
! 336: goto relebad;
! 337: #endif
! 338: goto sendsig;
! 339:
! 340: case PT_DETACH:
! 341: /*
! 342: * From the 4.4BSD PRM:
! 343: * "The data argument is taken as a signal number and the
! 344: * child's execution continues at location addr as if it
! 345: * incurred that signal. Normally the signal number will
! 346: * be either 0 to indicate that the signal that caused the
! 347: * stop should be ignored, or that value fetched out of
! 348: * the process's image indicating which signal caused
! 349: * the stop. If addr is (int *)1 then execution continues
! 350: * from where it stopped."
! 351: */
! 352:
! 353: /* Check that the data is a valid signal number or zero. */
! 354: if (SCARG(uap, data) < 0 || SCARG(uap, data) >= NSIG)
! 355: return (EINVAL);
! 356:
! 357: #ifdef PT_STEP
! 358: /*
! 359: * Arrange for a single-step, if that's requested and possible.
! 360: */
! 361: error = process_sstep(t, SCARG(uap, req) == PT_STEP);
! 362: if (error)
! 363: goto relebad;
! 364: #endif
! 365:
! 366: /* give process back to original parent or init */
! 367: if (t->p_oppid != t->p_pptr->p_pid) {
! 368: struct proc *pp;
! 369:
! 370: pp = pfind(t->p_oppid);
! 371: proc_reparent(t, pp ? pp : initproc);
! 372: }
! 373:
! 374: /* not being traced any more */
! 375: t->p_oppid = 0;
! 376: atomic_clearbits_int(&t->p_flag, P_TRACED|P_WAITED);
! 377:
! 378: sendsig:
! 379: bzero(t->p_ptstat, sizeof(*t->p_ptstat));
! 380:
! 381: /* Finally, deliver the requested signal (or none). */
! 382: if (t->p_stat == SSTOP) {
! 383: t->p_xstat = SCARG(uap, data);
! 384: SCHED_LOCK(s);
! 385: setrunnable(t);
! 386: SCHED_UNLOCK(s);
! 387: } else {
! 388: if (SCARG(uap, data) != 0)
! 389: psignal(t, SCARG(uap, data));
! 390: }
! 391: return (0);
! 392:
! 393: relebad:
! 394: return (error);
! 395:
! 396: case PT_KILL:
! 397: /* just send the process a KILL signal. */
! 398: SCARG(uap, data) = SIGKILL;
! 399: goto sendsig; /* in PT_CONTINUE, above. */
! 400:
! 401: case PT_ATTACH:
! 402: /*
! 403: * As done in procfs:
! 404: * Go ahead and set the trace flag.
! 405: * Save the old parent (it's reset in
! 406: * _DETACH, and also in kern_exit.c:wait4()
! 407: * Reparent the process so that the tracing
! 408: * proc gets to see all the action.
! 409: * Stop the target.
! 410: */
! 411: atomic_setbits_int(&t->p_flag, P_TRACED);
! 412: t->p_oppid = t->p_pptr->p_pid;
! 413: if (t->p_pptr != p)
! 414: proc_reparent(t, p);
! 415: if (t->p_ptstat == NULL)
! 416: t->p_ptstat = malloc(sizeof(*t->p_ptstat),
! 417: M_SUBPROC, M_WAITOK);
! 418: SCARG(uap, data) = SIGSTOP;
! 419: goto sendsig;
! 420:
! 421: case PT_GET_EVENT_MASK:
! 422: if (SCARG(uap, data) != sizeof(pe))
! 423: return (EINVAL);
! 424: bzero(&pe, sizeof(pe));
! 425: pe.pe_set_event = t->p_ptmask;
! 426: return (copyout(&pe, SCARG(uap, addr), sizeof(pe)));
! 427: case PT_SET_EVENT_MASK:
! 428: if (SCARG(uap, data) != sizeof(pe))
! 429: return (EINVAL);
! 430: if ((error = copyin(SCARG(uap, addr), &pe, sizeof(pe))))
! 431: return (error);
! 432: t->p_ptmask = pe.pe_set_event;
! 433: return (0);
! 434:
! 435: case PT_GET_PROCESS_STATE:
! 436: if (SCARG(uap, data) != sizeof(*t->p_ptstat))
! 437: return (EINVAL);
! 438: return (copyout(t->p_ptstat, SCARG(uap, addr),
! 439: sizeof(*t->p_ptstat)));
! 440:
! 441: case PT_SETREGS:
! 442: KASSERT((p->p_flag & P_SYSTEM) == 0);
! 443: if ((error = process_checkioperm(p, t)) != 0)
! 444: return (error);
! 445:
! 446: regs = malloc(sizeof(*regs), M_TEMP, M_WAITOK);
! 447: error = copyin(SCARG(uap, addr), regs, sizeof(*regs));
! 448: if (error == 0) {
! 449: error = process_write_regs(t, regs);
! 450: }
! 451: free(regs, M_TEMP);
! 452: return (error);
! 453: case PT_GETREGS:
! 454: KASSERT((p->p_flag & P_SYSTEM) == 0);
! 455: if ((error = process_checkioperm(p, t)) != 0)
! 456: return (error);
! 457:
! 458: regs = malloc(sizeof(*regs), M_TEMP, M_WAITOK);
! 459: error = process_read_regs(t, regs);
! 460: if (error == 0)
! 461: error = copyout(regs,
! 462: SCARG(uap, addr), sizeof (*regs));
! 463: free(regs, M_TEMP);
! 464: return (error);
! 465: #ifdef PT_SETFPREGS
! 466: case PT_SETFPREGS:
! 467: KASSERT((p->p_flag & P_SYSTEM) == 0);
! 468: if ((error = process_checkioperm(p, t)) != 0)
! 469: return (error);
! 470:
! 471: fpregs = malloc(sizeof(*fpregs), M_TEMP, M_WAITOK);
! 472: error = copyin(SCARG(uap, addr), fpregs, sizeof(*fpregs));
! 473: if (error == 0) {
! 474: error = process_write_fpregs(t, fpregs);
! 475: }
! 476: free(fpregs, M_TEMP);
! 477: return (error);
! 478: #endif
! 479: #ifdef PT_GETFPREGS
! 480: case PT_GETFPREGS:
! 481: KASSERT((p->p_flag & P_SYSTEM) == 0);
! 482: if ((error = process_checkioperm(p, t)) != 0)
! 483: return (error);
! 484:
! 485: fpregs = malloc(sizeof(*fpregs), M_TEMP, M_WAITOK);
! 486: error = process_read_fpregs(t, fpregs);
! 487: if (error == 0)
! 488: error = copyout(fpregs,
! 489: SCARG(uap, addr), sizeof(*fpregs));
! 490: free(fpregs, M_TEMP);
! 491: return (error);
! 492: #endif
! 493: #ifdef PT_SETXMMREGS
! 494: case PT_SETXMMREGS:
! 495: KASSERT((p->p_flag & P_SYSTEM) == 0);
! 496: if ((error = process_checkioperm(p, t)) != 0)
! 497: return (error);
! 498:
! 499: xmmregs = malloc(sizeof(*xmmregs), M_TEMP, M_WAITOK);
! 500: error = copyin(SCARG(uap, addr), xmmregs, sizeof(*xmmregs));
! 501: if (error == 0) {
! 502: error = process_write_xmmregs(t, xmmregs);
! 503: }
! 504: free(xmmregs, M_TEMP);
! 505: return (error);
! 506: #endif
! 507: #ifdef PT_GETXMMREGS
! 508: case PT_GETXMMREGS:
! 509: KASSERT((p->p_flag & P_SYSTEM) == 0);
! 510: if ((error = process_checkioperm(p, t)) != 0)
! 511: return (error);
! 512:
! 513: xmmregs = malloc(sizeof(*xmmregs), M_TEMP, M_WAITOK);
! 514: error = process_read_xmmregs(t, xmmregs);
! 515: if (error == 0)
! 516: error = copyout(xmmregs,
! 517: SCARG(uap, addr), sizeof(*xmmregs));
! 518: free(xmmregs, M_TEMP);
! 519: return (error);
! 520: #endif
! 521: #ifdef PT_WCOOKIE
! 522: case PT_WCOOKIE:
! 523: wcookie = process_get_wcookie (t);
! 524: return (copyout(&wcookie, SCARG(uap, addr),
! 525: sizeof (register_t)));
! 526: #endif
! 527: }
! 528:
! 529: #ifdef DIAGNOSTIC
! 530: panic("ptrace: impossible");
! 531: #endif
! 532: return 0;
! 533: }
! 534: #endif /* PTRACE */
! 535:
! 536: /*
! 537: * Check if a process is allowed to fiddle with the memory of another.
! 538: *
! 539: * p = tracer
! 540: * t = tracee
! 541: *
! 542: * 1. You can't attach to a process not owned by you or one that has raised
! 543: * its privileges.
! 544: * 1a. ...unless you are root.
! 545: *
! 546: * 2. init is always off-limits because it can control the securelevel.
! 547: * 2a. ...unless securelevel is permanently set to insecure.
! 548: *
! 549: * 3. Processes that are in the process of doing an exec() are always
! 550: * off-limits because of the can of worms they are. Just wait a
! 551: * second.
! 552: */
! 553: int
! 554: process_checkioperm(struct proc *p, struct proc *t)
! 555: {
! 556: int error;
! 557:
! 558: if ((t->p_cred->p_ruid != p->p_cred->p_ruid ||
! 559: ISSET(t->p_flag, P_SUGIDEXEC) ||
! 560: ISSET(t->p_flag, P_SUGID)) &&
! 561: (error = suser(p, 0)) != 0)
! 562: return (error);
! 563:
! 564: if ((t->p_pid == 1) && (securelevel > -1))
! 565: return (EPERM);
! 566:
! 567: if (t->p_flag & P_INEXEC)
! 568: return (EAGAIN);
! 569:
! 570: return (0);
! 571: }
! 572:
! 573: int
! 574: process_domem(struct proc *curp, struct proc *p, struct uio *uio, int req)
! 575: {
! 576: int error;
! 577: vaddr_t addr;
! 578: vsize_t len;
! 579:
! 580: len = uio->uio_resid;
! 581: if (len == 0)
! 582: return (0);
! 583:
! 584: if ((error = process_checkioperm(curp, p)) != 0)
! 585: return (error);
! 586:
! 587: /* XXXCDC: how should locking work here? */
! 588: if ((p->p_flag & P_WEXIT) || (p->p_vmspace->vm_refcnt < 1))
! 589: return(EFAULT);
! 590: addr = uio->uio_offset;
! 591: p->p_vmspace->vm_refcnt++; /* XXX */
! 592: error = uvm_io(&p->p_vmspace->vm_map, uio,
! 593: (req == PT_WRITE_I) ? UVM_IO_FIXPROT : 0);
! 594: uvmspace_free(p->p_vmspace);
! 595:
! 596: if (error == 0 && req == PT_WRITE_I)
! 597: pmap_proc_iflush(p, addr, len);
! 598:
! 599: return (error);
! 600: }
CVSweb