2021-10-11 18:37:13 -03:00

624 lines
13 KiB
C

/* @(#)sys_process.c 1.1 94/10/31 SMI; from UCB 5.10 83/07/01 */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/vm.h>
#include <sys/buf.h>
#include <sys/acct.h>
#include <sys/uio.h>
#include <sys/ptrace.h>
#include <sys/mman.h>
#include <machine/reg.h>
#include <machine/psl.h>
#include <machine/pte.h>
#include <vm/hat.h>
#include <vm/as.h>
#include <machine/seg_kmem.h>
/*
* 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);
}