/* @(#)sys_process.c 1.1 94/10/31 SMI; from UCB 5.10 83/07/01 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Priority for tracing */ #define IPCPRI PZERO #ifdef vax #define NIPCREG 16 #endif #ifdef mc68000 #define NIPCREG 17 #endif #ifdef sparc #define NIPCREG 17 #endif #ifdef i386 #define NIPCREG 8 #endif int ipcreg[NIPCREG] = #ifdef vax {R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, AP, FP, SP, PC}; #endif #ifdef mc68000 {R0, R1, R2, R3, R4, R5, R6, R7, AR0, AR1, AR2, AR3, AR4, AR5, AR6, AR7, PC}; #endif #ifdef sparc {PC, nPC, G1, G2, G3, G4, G5, G6, G7, O0, O1, O2, O3, O4, O5, O6, O7}; #endif #ifdef i386 {EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI}; /* ZZZ */ #endif #define PHYSOFF(p, o) \ ((physadr)(p)+((o)/sizeof (((physadr)0)->r[0]))) struct ipc ipc; /* * sys-trace system call. */ ptrace() { register struct proc *p; register struct a { enum ptracereq req; int pid; caddr_t addr; int data; caddr_t addr2; } *uap; uap = (struct a *)u.u_ap; if (uap->req == PTRACE_TRACEME) { u.u_procp->p_flag |= STRC; u.u_procp->p_tptr = u.u_procp->p_pptr; u.u_procp->p_pptr->p_flag |= STRACNG; return; } p = pfind(uap->pid); if (p == NULL || p->p_as == NULL) { u.u_error = ESRCH; return; } if (uap->req == PTRACE_ATTACH) { /* * Can only attach if: * process is not already being traced, and * process does not have any privileges beyond * what our effective privileges already imply * (i.e., our effective [ug]id matches his real * and saved [ug]ids, or we're superuser). */ if ((p->p_flag & STRC) || ((u.u_uid != p->p_suid || u.u_uid != p->p_uid || u.u_gid != p->p_sgid || u.u_gid != p->p_cred->cr_rgid) && !suser())) { u.u_error = EPERM; return; } /* * Tell him we're tracing him and stop him where he is. */ p->p_flag |= STRC; p->p_tptr = u.u_procp; /* if the traced process already stop, wake it up first */ if (p->p_stat == SSTOP) { int s; psignal(p, SIGCONT); /* * We need to force the process to sleep because * psignal() ignores all signals while p_flag = STRC. */ s = splhigh(); if (p->p_stat == SSTOP) p->p_stat = SSLEEP; (void) splx(s); } /* clear any pending STOP or CONT signal */ if (p->p_cursig == SIGSTOP || p->p_cursig == SIGCONT) p->p_cursig = 0; psignal(p, SIGSTOP); u.u_procp->p_flag |= STRACNG; return; } if (p->p_tptr != u.u_procp || !(p->p_flag & STRC)) { u.u_error = ESRCH; return; } if (uap->req == PTRACE_DETACH && p->p_stat != SSTOP) { /* If not stopped, ignore addr and sig */ p->p_tptr = 0; p->p_flag &= ~(STRC | STRCSYS); return; } if (p->p_stat != SSTOP) { u.u_error = ESRCH; return; } #ifdef sparc /* * For sparc, the pc must be aligned to a four byte boundary. * Special case: addr == 1, continue where we were. */ if ((uap->req == PTRACE_CONT) && ((int)uap->addr & 0x3) && ((int)uap->addr != 1)) { u.u_error = EINVAL; return; } #endif sparc while (ipc.ip_lock) (void) sleep((caddr_t)&ipc, IPCPRI); ipc.ip_lock = p->p_pid; ipc.ip_error = 0; ipc.ip_data = uap->data; ipc.ip_addr = uap->addr; ipc.ip_req = uap->req; ipc.ip_addr2 = uap->addr2; p->p_flag &= ~SWTED; u.u_r.r_val1 = 0; switch (uap->req) { case PTRACE_GETREGS: runchild(p); if (copyout((caddr_t)&ipc.ip_regs, ipc.ip_addr, sizeof (ipc.ip_regs)) != 0) { ipc.ip_error = 1; } break; case PTRACE_SETREGS: if (copyin(ipc.ip_addr, (caddr_t)&ipc.ip_regs, sizeof (ipc.ip_regs)) != 0) { ipc.ip_error = 1; } #ifdef sparc if ((ipc.ip_regs.r_pc & 03) || (ipc.ip_regs.r_npc & 03)) { ipc.ip_error = 1; break; } #endif sparc runchild(p); break; #ifdef sun #ifdef FPU case PTRACE_GETFPREGS: case PTRACE_SETFPREGS: case PTRACE_GETFPAREGS: case PTRACE_SETFPAREGS: fpu_ptrace(uap->req, p, &ipc); break; #endif FPU #endif sun case PTRACE_READDATA: case PTRACE_READTEXT: ipc.ip_bigbuf = (caddr_t)new_kmem_alloc(CLBYTES, KMEM_SLEEP); while (ipc.ip_data > 0) { ipc.ip_req = uap->req; ipc.ip_nbytes = MIN(ipc.ip_data, CLBYTES); runchild(p); if (copyout(ipc.ip_bigbuf, ipc.ip_addr2, (u_int)ipc.ip_nbytes) != 0) { ipc.ip_error = 1; break; } ipc.ip_addr += CLBYTES; ipc.ip_data -= CLBYTES; ipc.ip_addr2 += CLBYTES; } kmem_free(ipc.ip_bigbuf, CLBYTES); ipc.ip_bigbuf = NULL; break; case PTRACE_WRITEDATA: case PTRACE_WRITETEXT: ipc.ip_bigbuf = new_kmem_alloc(CLBYTES, KMEM_SLEEP); while (ipc.ip_data > 0) { ipc.ip_req = uap->req; ipc.ip_nbytes = MIN(ipc.ip_data, CLBYTES); if (copyin(ipc.ip_addr2, ipc.ip_bigbuf, (u_int)ipc.ip_nbytes) != 0) { ipc.ip_error = 1; break; } runchild(p); ipc.ip_addr += CLBYTES; ipc.ip_data -= CLBYTES; } kmem_free(ipc.ip_bigbuf, CLBYTES); ipc.ip_bigbuf = NULL; break; case PTRACE_DUMPCORE: { struct vattr vattr; vattr_null(&vattr); vattr.va_type = VREG; vattr.va_mode = 0644; u.u_error = vn_create(ipc.ip_addr, UIO_USERSPACE, &vattr, NONEXCL, VWRITE, &ipc.ip_vp); if (u.u_error) { ipc.ip_error = 1; break; } if (vattr.va_nlink != 1) { u.u_error = EFAULT; VN_RELE(ipc.ip_vp); break; } runchild(p); } break; default: runchild(p); u.u_r.r_val1 = ipc.ip_data; break; } if (ipc.ip_error != 0) u.u_error = EIO; ipc.ip_lock = 0; wakeup((caddr_t)&ipc); } /* * Set the child as runnable and go to sleep waiting for him * to do his part. */ runchild(p) struct proc *p; { while ((int)ipc.ip_req > (int)PTRACE_CHILDDONE) { if (p->p_stat == SSTOP) setrun(p); (void) sleep((caddr_t)&ipc, IPCPRI); } } /* * Code that the child process * executes to implement the command * of the parent process in tracing. */ procxmt() { register enum ptracereq req; register int i, c; register int *p; register char *cp; register caddr_t a; register caddr_t addr; int chgprot; struct as *as = u.u_procp->p_as; #ifdef i386 long debugr7; #endif if (ipc.ip_lock != u.u_procp->p_pid) return (0); u.u_procp->p_slptime = 0; req = ipc.ip_req; ipc.ip_req = PTRACE_CHILDDONE; addr = ipc.ip_addr; switch (req) { /* read user I */ case PTRACE_PEEKTEXT: if (!useracc(addr, NBPW, B_READ)) goto error; ipc.ip_data = fuiword(addr); break; /* read user D */ case PTRACE_PEEKDATA: if (!useracc(addr, NBPW, B_READ)) goto error; ipc.ip_data = fuword(addr); break; /* read u */ case PTRACE_PEEKUSER: /* * Make sure that the requested access is within this * user's u-area and that it's properly accessible. */ i = (int)addr; if (i < 0 || i > (sizeof (struct user) - sizeof (int))) goto error; a = (caddr_t)PHYSOFF(uunix, i); if (as_checkprot(&kas, (addr_t)a, sizeof (int), PROT_READ) != A_SUCCESS) goto error; ipc.ip_data = *(int *)a; break; /* write user I */ case PTRACE_POKETEXT: if ((i = suiword(addr, ipc.ip_data)) < 0) { /* * Must change the permissions to allow writing. * The act of writing should cause a copy-on-write * for a ZMAGIC file mapped with PRIVATE text. */ if (as_setprot(as, addr, NBPW, PROT_ALL) == A_SUCCESS) i = suiword(addr, ipc.ip_data); /* XXX - this assumes the old permissions */ (void) as_setprot(as, addr, NBPW, PROT_ALL & ~PROT_WRITE); } if (i < 0) goto error; break; /* write user D */ case PTRACE_POKEDATA: if (suword(addr, 0) < 0) goto error; (void) suword(addr, ipc.ip_data); break; /* write u */ case PTRACE_POKEUSER: i = (int)addr; p = (int *)PHYSOFF(&u, i); for (i = 0; i < NIPCREG; i++) if (p == &u.u_ar0[ipcreg[i]]) goto ok; if (p == &u.u_ar0[PS]) { ipc.ip_data = (u.u_ar0[PS] & ~PSL_USERMASK) | (ipc.ip_data & PSL_USERMASK); goto ok; } goto error; ok: /* make a final paranoid check. */ if (as_checkprot(&kas, (addr_t)p, sizeof (int), PROT_WRITE) != A_SUCCESS) goto error; *p = ipc.ip_data; break; /* set signal and continue */ /* one version causes a trace-trap */ case PTRACE_SINGLESTEP: /* one version stops tracing */ #ifndef PSL_T goto error; #endif !PSL_T case PTRACE_DETACH: case PTRACE_CONT: case PTRACE_SYSCALL: if ((int)addr != 1) { u.u_ar0[PC] = (int)addr; #ifdef sparc u.u_ar0[nPC] = (int)addr + 4; #endif sparc } if ((unsigned)ipc.ip_data > NSIG) goto error; u.u_procp->p_cursig = ipc.ip_data; /* see issig */ #ifdef PSL_T if (req == PTRACE_SINGLESTEP) u.u_ar0[PS] |= PSL_T; else #endif PSL_T if (req == PTRACE_DETACH) { u.u_procp->p_flag &= ~STRC; u.u_procp->p_flag &= ~STRCSYS; u.u_procp->p_tptr = 0; } else if (req == PTRACE_SYSCALL) u.u_procp->p_flag |= STRCSYS; wakeup((caddr_t)&ipc); return (1); /* force exit */ case PTRACE_KILL: u.u_procp->p_flag &= ~(STRC|STRCSYS); u.u_procp->p_tptr = 0; wakeup((caddr_t)&ipc); exit(u.u_procp->p_cursig); /* read registers - copy from the u area to the ipc buffer */ case PTRACE_GETREGS: ipc.ip_regs = *(struct regs *) u.u_ar0; break; /* write registers - copy from the ipc buffer to the u area */ case PTRACE_SETREGS: ipc.ip_regs.r_ps = (u.u_ar0[PS] & ~PSL_USERMASK) | (ipc.ip_regs.r_ps & PSL_USERMASK); *(struct regs *) u.u_ar0 = ipc.ip_regs; break; /* read data segment buffer */ case PTRACE_READDATA: if (copyin(addr, ipc.ip_bigbuf, (u_int)ipc.ip_nbytes) != 0) goto error; break; /* write data segment buffer */ case PTRACE_WRITEDATA: if (copyout(ipc.ip_bigbuf, addr, (u_int)ipc.ip_nbytes) != 0) goto error; break; /* read text segment buffer */ case PTRACE_READTEXT: if (!useracc(addr, (u_int)ipc.ip_nbytes, B_READ)) goto error; a = addr; /* user address */ c = 0; /* total bytes moved */ if (((int)a & (NBPW - 1)) == 0) { /* * a is word-aligned, so we can copy in words */ for (p = (int *)ipc.ip_bigbuf; c < ipc.ip_nbytes - NBPW + 1; c += NBPW, a += NBPW, p++) *p = fuiword(a); for (cp = (char *)p; c < ipc.ip_nbytes; c++, a++, cp++) *cp = fuibyte(a); } else { /* * a isn't word aligned, so we copy byte by byte * (bigbuf and a will nevery have the same alignment) */ for (cp = ipc.ip_bigbuf; c < ipc.ip_nbytes; c++, a++, cp++) *cp = fuibyte(a); } break; /* write text segment buffer */ case PTRACE_WRITETEXT: if (useracc(addr, (u_int)ipc.ip_nbytes, B_WRITE)) { chgprot = 0; /* permissions ok */ } else { /* * Must change the permissions to allow writing. * The act of writing should cause a copy-on-write * for a ZMAGIC file mapped with PRIVATE text. */ if (as_setprot(as, addr, (u_int)ipc.ip_nbytes, PROT_ALL) != A_SUCCESS) goto error; chgprot = 1; /* changed permissions */ } a = addr; /* user address */ c = 0; /* total bytes moved */ if (((int)a & (NBPW - 1)) == 0) { for (p = (int *)ipc.ip_bigbuf; c < ipc.ip_nbytes - NBPW + 1; c += NBPW, a += NBPW, p++) { i = suiword(a, *p); if (i < 0) goto writetextout; } for (cp = (char *)p; c < ipc.ip_nbytes; c++, a++, cp++) { i = suibyte(a, *cp); if (i < 0) goto writetextout; } } else { for (cp = ipc.ip_bigbuf; c < ipc.ip_nbytes; c++, a++, cp++) { i = suibyte(a, *cp); if (i < 0) break; } } writetextout: if (chgprot) { /* XXX - this assumes the old permissions */ (void) as_setprot(as, addr, (u_int)ipc.ip_nbytes, PROT_ALL & ~PROT_WRITE); } if (i < 0) goto error; break; #ifdef sun #ifdef FPU case PTRACE_GETFPREGS: case PTRACE_SETFPREGS: case PTRACE_GETFPAREGS: case PTRACE_SETFPAREGS: if (-1 == fpu_procxmt(req, &ipc)) goto error; break; #endif FPU #endif sun #ifdef i386 #define BPWRITE 1 #define BPACCESS 3 /* * Set write and access breakpoints. Also sets necessary bits * in DR7; data is the breakpoint number, and addr2 is the * length of the operand. */ case PTRACE_SETWRBKPT: case PTRACE_SETACBKPT: if ((i = (int)ipc.ip_addr2) != 1 && i != 2 && i != 4) { ipc.ip_error = 1; break; } switch (ipc.ip_data) { case 0: _wdr0(ipc.ip_addr); break; case 1: _wdr1(ipc.ip_addr); break; case 2: _wdr2(ipc.ip_addr); break; case 3: _wdr3(ipc.ip_addr); break; default: ipc.ip_error = 1; break; } if (ipc.ip_error) break; debugr7 = _dr7(); i = ipc.ip_data; debugr7 &= ~(0xf << (16 + i * 4) | 3 << (i * 2)); if (req == PTRACE_SETWRBKPT) c = BPWRITE; else c = BPACCESS; debugr7 |= c << (16 + i * 4) | ((int)ipc.ip_addr2 - 1) << (18 + i * 4) | 1 << (i * 2) | 0x100; _wdr7(debugr7); break; /* Clear debug register 7 */ case PTRACE_CLRDR7: _wdr7(0); break; #endif i386 /* Dump core image to file */ case PTRACE_DUMPCORE: if (core(ipc.ip_vp)) break; /* get u.u_code */ case PTRACE_GETUCODE: ipc.ip_data = u.u_code; break; default: error: ipc.ip_error = 1; } wakeup((caddr_t)&ipc); return (0); }