📄 pcontrol.c
字号:
default: dprintf("Pgrab: failed to open %s: %s\n", procname, strerror(errno)); rc = G_STRANGE; break; } goto err; } P->asfd = fd; (void) strcpy(fname, "status"); if ((fd = open(procname, O_RDONLY)) < 0 || (fd = dupfd(fd, 0)) < 0) { switch (errno) { case ENOENT: rc = G_NOPROC; break; default: dprintf("Pgrab: failed to open %s: %s\n", procname, strerror(errno)); rc = G_STRANGE; break; } goto err; } P->statfd = fd; if (!(flags & PGRAB_RDONLY)) { (void) strcpy(fname, "ctl"); if ((fd = open(procname, O_WRONLY)) < 0 || (fd = dupfd(fd, 0)) < 0) { switch (errno) { case ENOENT: rc = G_NOPROC; break; default: dprintf("Pgrab: failed to open %s: %s\n", procname, strerror(errno)); rc = G_STRANGE; break; } goto err; } P->ctlfd = fd; } P->state = PS_RUN; P->pid = pid; /* * We are now in the Window of Vulnerability (WoV). The process may * exec() a setuid/setgid or unreadable object file between the open() * and the PCSTOP. We will get EAGAIN in this case and must start over. * As Pstopstatus will trigger the first read() from a /proc file, * we also need to handle EOVERFLOW here when 32-bit as an indicator * that this process is 64-bit. Finally, if the process has become * a zombie (PS_UNDEAD) while we were trying to grab it, just remain * silent about this and pretend there was no process. */ if (Pstopstatus(P, PCNULL, 0) != 0) {#ifndef _LP64 if (errno == EOVERFLOW) { rc = G_LP64; goto err; }#endif if (P->state == PS_LOST) { /* WoV */ (void) mutex_destroy(&P->proc_lock); goto again; } if (P->state == PS_UNDEAD) rc = G_NOPROC; else rc = G_STRANGE; goto err; } /* * If the process is a system process, we can't control it even as root */ if (P->status.pr_flags & PR_ISSYS) { rc = G_SYS; goto err; }#ifndef _LP64 /* * We must be a 64-bit process to deal with a 64-bit process */ if (P->status.pr_dmodel == PR_MODEL_LP64) { rc = G_LP64; goto err; }#endif /* * Remember the status for use by Prelease(). */ P->orig_status = P->status; /* structure copy */ /* * Before stopping the process, make sure we are not grabbing ourselves. * If we are, make sure we are doing it PGRAB_RDONLY. */ if (pid == getpid()) { /* * Verify that the process is really ourself: * Set a magic number, read it through the * /proc file and see if the results match. */ uint32_t magic1 = 0; uint32_t magic2 = 2; errno = 0; if (Pread(P, &magic2, sizeof (magic2), (uintptr_t)&magic1) == sizeof (magic2) && magic2 == 0 && (magic1 = 0xfeedbeef) && Pread(P, &magic2, sizeof (magic2), (uintptr_t)&magic1) == sizeof (magic2) && magic2 == 0xfeedbeef && !(flags & PGRAB_RDONLY)) { rc = G_SELF; goto err; } } /* * If the process is already stopped or has been directed * to stop via /proc, do not set run-on-last-close. */ if (!(P->status.pr_lwp.pr_flags & (PR_ISTOP|PR_DSTOP)) && !(flags & PGRAB_RDONLY)) { /* * Mark the process run-on-last-close so * it runs even if we die from SIGKILL. */ if (Psetflags(P, PR_RLC) != 0) { if (errno == EAGAIN) { /* WoV */ (void) mutex_destroy(&P->proc_lock); goto again; } if (errno == ENOENT) /* No complaint about zombies */ rc = G_ZOMB; else { dprintf("Pgrab: failed to set RLC\n"); rc = G_STRANGE; } goto err; } } /* * If a stop directive is pending and the process has not yet stopped, * then synchronously wait for the stop directive to take effect. * Limit the time spent waiting for the process to stop by iterating * at most 10 times. The time-out of 20 ms corresponds to the time * between sending the stop directive and the process actually stopped * as measured by DTrace on a slow, busy system. If the process doesn't * stop voluntarily, clear the PR_DSTOP flag so that the code below * forces the process to stop. */ if (!(flags & PGRAB_RDONLY)) { int niter = 0; while ((P->status.pr_lwp.pr_flags & (PR_STOPPED|PR_DSTOP)) == PR_DSTOP && niter < 10 && Pstopstatus(P, PCTWSTOP, 20) != 0) { niter++; if (flags & PGRAB_NOSTOP) break; } if (niter == 10 && !(flags & PGRAB_NOSTOP)) { /* Try it harder down below */ P->status.pr_lwp.pr_flags &= ~PR_DSTOP; } } /* * If the process is not already stopped or directed to stop * and PGRAB_NOSTOP was not specified, stop the process now. */ if (!(P->status.pr_lwp.pr_flags & (PR_ISTOP|PR_DSTOP)) && !(flags & PGRAB_NOSTOP)) { /* * Stop the process, get its status and signal/syscall masks. */ if (((P->status.pr_lwp.pr_flags & PR_STOPPED) && Pstopstatus(P, PCDSTOP, 0) != 0) || Pstopstatus(P, PCSTOP, 2000) != 0) {#ifndef _LP64 if (errno == EOVERFLOW) { rc = G_LP64; goto err; }#endif if (P->state == PS_LOST) { /* WoV */ (void) mutex_destroy(&P->proc_lock); goto again; } if ((errno != EINTR && errno != ERESTART) || (P->state != PS_STOP && !(P->status.pr_flags & PR_DSTOP))) { if (P->state != PS_RUN && errno != ENOENT) { dprintf("Pgrab: failed to PCSTOP\n"); rc = G_STRANGE; } else { rc = G_ZOMB; } goto err; } } /* * Process should now either be stopped via /proc or there * should be an outstanding stop directive. */ if (!(P->status.pr_flags & (PR_ISTOP|PR_DSTOP))) { dprintf("Pgrab: process is not stopped\n"); rc = G_STRANGE; goto err; }#ifndef _LP64 /* * Test this again now because the 32-bit victim process may * have exec'd a 64-bit process in the meantime. */ if (P->status.pr_dmodel == PR_MODEL_LP64) { rc = G_LP64; goto err; }#endif } /* * Cancel all tracing flags unless the PGRAB_RETAIN flag is set. */ if (!(flags & PGRAB_RETAIN)) { (void) Psysentry(P, 0, FALSE); (void) Psysexit(P, 0, FALSE); (void) Psignal(P, 0, FALSE); (void) Pfault(P, 0, FALSE); Psync(P); } *perr = 0; return (P);err: Pfree(P); *perr = rc; return (NULL);}/* * Return a printable string corresponding to a Pgrab() error return. */const char *Pgrab_error(int error){ const char *str; switch (error) { case G_NOPROC: str = "no such process"; break; case G_NOCORE: str = "no such core file"; break; case G_NOPROCORCORE: str = "no such process or core file"; break; case G_NOEXEC: str = "cannot find executable file"; break; case G_ZOMB: str = "zombie process"; break; case G_PERM: str = "permission denied"; break; case G_BUSY: str = "process is traced"; break; case G_SYS: str = "system process"; break; case G_SELF: str = "attempt to grab self"; break; case G_INTR: str = "operation interrupted"; break; case G_LP64: str = "program is _LP64, self is not"; break; case G_FORMAT: str = "file is not an ELF core file"; break; case G_ELF: str = "libelf error"; break; case G_NOTE: str = "core file is corrupt or missing required data"; break; case G_STRANGE: str = "unanticipated system error"; break; case G_ISAINVAL: str = "wrong ELF machine type"; break; case G_BADLWPS: str = "bad lwp specification"; break; default: str = "unknown error"; break; } return (str);}/* * Free a process control structure. * Close the file descriptors but don't do the Prelease logic. */voidPfree(struct ps_prochandle *P){ uint_t i; if (P->core != NULL) { extern void __priv_free_info(void *); lwp_info_t *nlwp, *lwp = list_next(&P->core->core_lwp_head); for (i = 0; i < P->core->core_nlwp; i++, lwp = nlwp) { nlwp = list_next(lwp);#ifdef __sparc if (lwp->lwp_gwins != NULL) free(lwp->lwp_gwins); if (lwp->lwp_xregs != NULL) free(lwp->lwp_xregs); if (lwp->lwp_asrs != NULL) free(lwp->lwp_asrs);#endif free(lwp); } if (P->core->core_platform != NULL) free(P->core->core_platform); if (P->core->core_uts != NULL) free(P->core->core_uts); if (P->core->core_cred != NULL) free(P->core->core_cred); if (P->core->core_priv != NULL) free(P->core->core_priv); if (P->core->core_privinfo != NULL) __priv_free_info(P->core->core_privinfo); if (P->core->core_ppii != NULL) free(P->core->core_ppii); if (P->core->core_zonename != NULL) free(P->core->core_zonename);#if defined(__i386) || defined(__amd64) if (P->core->core_ldt != NULL) free(P->core->core_ldt);#endif free(P->core); } if (P->ucaddrs != NULL) { free(P->ucaddrs); P->ucaddrs = NULL; P->ucnelems = 0; } (void) mutex_lock(&P->proc_lock); if (P->hashtab != NULL) { struct ps_lwphandle *L; for (i = 0; i < HASHSIZE; i++) { while ((L = P->hashtab[i]) != NULL) Lfree_internal(P, L); } free(P->hashtab); } (void) mutex_unlock(&P->proc_lock); (void) mutex_destroy(&P->proc_lock); if (P->agentctlfd >= 0) (void) close(P->agentctlfd); if (P->agentstatfd >= 0) (void) close(P->agentstatfd); if (P->ctlfd >= 0) (void) close(P->ctlfd); if (P->asfd >= 0) (void) close(P->asfd); if (P->statfd >= 0) (void) close(P->statfd); Preset_maps(P); /* clear out the structure as a precaution against reuse */ (void) memset(P, 0, sizeof (*P)); P->ctlfd = -1; P->asfd = -1; P->statfd = -1; P->agentctlfd = -1; P->agentstatfd = -1; free(P);}/* * Return the state of the process, one of the PS_* values. */intPstate(struct ps_prochandle *P){ return (P->state);}/* * Return the open address space file descriptor for the process. * Clients must not close this file descriptor, not use it * after the process is freed. */intPasfd(struct ps_prochandle *P){ return (P->asfd);}/* * Return the open control file descriptor for the process. * Clients must not close this file descriptor, not use it * after the process is freed. */intPctlfd(struct ps_prochandle *P){ return (P->ctlfd);}/* * Return a pointer to the process psinfo structure. * Clients should not hold on to this pointer indefinitely. * It will become invalid on Prelease(). */const psinfo_t *Ppsinfo(struct ps_prochandle *P){ if (P->state == PS_IDLE) { errno = ENODATA; return (NULL); } if (P->state != PS_DEAD && proc_get_psinfo(P->pid, &P->psinfo) == -1) return (NULL); return (&P->psinfo);}/* * Return a pointer to the process status structure. * Clients should not hold on to this pointer indefinitely. * It will become invalid on Prelease(). */const pstatus_t *Pstatus(struct ps_prochandle *P){ return (&P->status);}/* * Fill in a pointer to a process credentials structure. The ngroups parameter * is the number of supplementary group entries allocated in the caller's cred * structure. It should equal zero or one unless extra space has been * allocated for the group list by the caller. */intPcred(struct ps_prochandle *P, prcred_t *pcrp, int ngroups){ if (P->state == PS_IDLE) { errno = ENODATA; return (-1); } if (P->state != PS_DEAD) return (proc_get_cred(P->pid, pcrp, ngroups)); if (P->core->core_cred != NULL) { /* * Avoid returning more supplementary group data than the * caller has allocated in their buffer. We expect them to * check pr_ngroups afterward and potentially call us again. */ ngroups = MIN(ngroups, P->core->core_cred->pr_ngroups); (void) memcpy(pcrp, P->core->core_cred, sizeof (prcred_t) + (ngroups - 1) * sizeof (gid_t)); return (0); } errno = ENODATA; return (-1);}#if defined(__i386) || defined(__amd64)/* * Fill in a pointer to a process LDT structure. * The caller provides a buffer of size 'nldt * sizeof (struct ssd)'; * If pldt == NULL or nldt == 0, we return the number of existing LDT entries. * Otherwise we return the actual number of LDT entries fetched (<= nldt). */intPldt(struct ps_prochandle *P, struct ssd *pldt, int nldt){ if (P->state == PS_IDLE) { errno = ENODATA; return (-1); } if (P->state != PS_DEAD) return (proc_get_ldt(P->pid, pldt, nldt)); if (pldt == NULL || nldt == 0) return (P->core->core_nldt); if (P->core->core_ldt != NULL) { nldt = MIN(nldt, P->core->core_nldt); (void) memcpy(pldt, P->core->core_ldt, nldt * sizeof (struct ssd)); return (nldt); } errno = ENODATA; return (-1);}#endif /* __i386 *//* * Fill in a pointer to a process privilege structure. */ssize_tPpriv(struct ps_prochandle *P, prpriv_t *pprv, size_t size){ if (P->state != PS_DEAD) { prpriv_t *pp = proc_get_priv(P->pid); if (pp != NULL) { size = MIN(size, PRIV_PRPRIV_SIZE(pp)); (void) memcpy(pprv, pp, size); free(pp); return (size); } return (-1); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -