#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h>
#include <err.h>
#include <errno.h>
#include <libproc.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <sys/stat.h>
extern int _libproc_test_fail_copyinargs;
static uint_t at_timeout = 5 * 1000;
static bool
agent_absent(struct ps_prochandle *P)
{
char path[PATH_MAX];
struct stat st;
if (Pstatus(P)->pr_agentid != 0) {
warnx("TEST FAILED: pstatus reports an agent lwp (id %d)",
(int)Pstatus(P)->pr_agentid);
return (false);
}
(void) snprintf(path, sizeof (path), "/proc/%d/lwp/agent",
(int)Pstatus(P)->pr_pid);
if (stat(path, &st) == 0) {
warnx("TEST FAILED: %s exists: the agent lwp was orphaned",
path);
return (false);
} else if (errno != ENOENT) {
warn("TEST FAILED: unexpected error from stat(%s)", path);
return (false);
}
return (true);
}
static bool
agent_checks(struct ps_prochandle *P)
{
bool ret = true;
struct rlimit rl;
if (pr_getrlimit(P, RLIMIT_NOFILE, &rl) != 0) {
warn("TEST FAILED: baseline pr_getrlimit() injection failed");
ret = false;
} else {
(void) printf("TEST PASSED: baseline injection succeeded\n");
}
if (!agent_absent(P))
ret = false;
_libproc_test_fail_copyinargs = 1;
if (pr_getrlimit(P, RLIMIT_NOFILE, &rl) == 0) {
warnx("TEST FAILED: injection unexpectedly succeeded with "
"_libproc_test_fail_copyinargs set");
ret = false;
} else {
(void) printf("TEST PASSED: injection failed with "
"_libproc_test_fail_copyinargs set\n");
}
_libproc_test_fail_copyinargs = 0;
if (!agent_absent(P)) {
ret = false;
} else {
(void) printf("TEST PASSED: agent destroyed after failed "
"injection\n");
}
if (pr_getrlimit(P, RLIMIT_NOFILE, &rl) != 0) {
warn("TEST FAILED: pr_getrlimit() injection failed following "
"recovery");
ret = false;
} else {
(void) printf("TEST PASSED: injection succeeded following "
"recovery\n");
}
if (!agent_absent(P))
ret = false;
return (ret);
}
int
main(int argc, char *argv[])
{
int ret = EXIT_SUCCESS, perr, wstat;
struct ps_prochandle *P;
GElf_Sym sym;
ulong_t bkpt;
pid_t pid;
if (argc != 2) {
errx(EXIT_FAILURE, "missing required program to inject "
"against");
}
P = Pcreate(argv[1], &argv[1], &perr, NULL, 0);
if (P == NULL) {
errx(EXIT_FAILURE, "failed to create %s: %s (0x%x)", argv[1],
Pcreate_error(perr), perr);
}
(void) Punsetflags(P, PR_RLC);
if (Psetflags(P, PR_KLC | PR_BPTADJ) != 0) {
int e = errno;
Prelease(P, PRELEASE_KILL);
errc(EXIT_FAILURE, e, "failed to set PR_KLC | PR_BPTADJ flags");
}
if (Pxlookup_by_name(P, LM_ID_BASE, PR_OBJ_EXEC, "agent_target_hook",
&sym, NULL) != 0) {
err(EXIT_FAILURE, "failed to find agent_target_hook symbol");
}
pid = Ppsinfo(P)->pr_pid;
if (Pfault(P, FLTBPT, 1) != 0)
errx(EXIT_FAILURE, "failed to set the FLTBPT disposition");
if (Psetbkpt(P, sym.st_value, &bkpt) != 0) {
err(EXIT_FAILURE, "failed to set breakpoint on "
"agent_target_hook (0x%" PRIx64 ")", sym.st_value);
}
if (Psetrun(P, 0, 0) != 0)
err(EXIT_FAILURE, "failed to resume running our target");
if (Pwait(P, at_timeout) != 0) {
err(EXIT_FAILURE, "%s did not hit our expected breakpoint",
argv[1]);
}
if (!agent_checks(P))
ret = EXIT_FAILURE;
if (Pdelbkpt(P, sym.st_value, bkpt) != 0)
err(EXIT_FAILURE, "failed to delete breakpoint");
if (Psetrun(P, 0, PRCFAULT) != 0)
err(EXIT_FAILURE, "failed to resume running our target");
if (waitpid(pid, &wstat, 0) != pid) {
err(EXIT_FAILURE, "failed to get our %s's (%" _PRIdID "), "
"wait info", argv[1], pid);
}
if (WIFEXITED(wstat) == 0)
errx(EXIT_FAILURE, "%s didn't actually exit!", argv[1]);
if (WEXITSTATUS(wstat) != 0) {
errx(EXIT_FAILURE, "%s failed with 0x%x", argv[1],
WEXITSTATUS(wstat));
} else {
(void) printf("TEST PASSED: target ran to completion "
"undamaged\n");
}
if (ret == EXIT_SUCCESS)
(void) printf("All tests passed successfully\n");
return (ret);
}