blob: d3c8e1e3e85b6d2ac476c7bbd693ff1eb1a39dc0 [file] [log] [blame]
/* -*- Mode: C; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
#include "util.h"
#include "ptrace_util.h"
static size_t read_all(int fd, char* buf, size_t size) {
size_t total = 0;
while (size > 0) {
ssize_t ret = read(fd, buf, size);
test_assert(ret >= 0);
if (ret == 0) {
return total;
}
size -= ret;
buf += ret;
total += ret;
}
return total;
}
static int proc_num_args(pid_t pid) {
char buf[4096];
int fd;
int i = 0;
int count = 0;
sprintf(buf, "/proc/%d/cmdline", pid);
fd = open(buf, O_RDONLY);
test_assert(fd >= 0);
size_t bytes = read_all(fd, buf, sizeof(buf));
// The kernel is supposed to append a zero-length string after all other
// command-line parameters, but it doesn't.
while ((size_t)i < bytes) {
++count;
i += strlen(buf + i) + 1;
}
test_assert(0 == close(fd));
return count;
}
void run_test(char** argv, int use_traceexec, int syscall_step)
{
pid_t child;
int status;
struct user_regs_struct regs;
if (0 == (child = fork())) {
char* args[] = { argv[0], "hello", NULL };
kill(getpid(), SIGSTOP);
execve(argv[0], args, environ);
/* should never reach here */
test_assert(0);
return;
}
test_assert(proc_num_args(child) == 1);
if (use_traceexec)
test_assert(0 == ptrace(PTRACE_SEIZE, child, NULL, (void*)PTRACE_O_TRACEEXEC));
else
test_assert(0 == ptrace(PTRACE_ATTACH, child, NULL,
(void*)PTRACE_O_TRACEEXEC));
test_assert(child == waitpid(child, &status, 0));
test_assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
if (syscall_step) {
while (1) {
test_assert(0 == ptrace(PTRACE_SYSCALL, child, NULL, (void*)0));
test_assert(child == waitpid(child, &status, 0));
if (!(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) &&
!(status == ((SIGTRAP << 8) | 0x7f)))
break;
}
} else {
while (1) {
test_assert(0 == ptrace(PTRACE_CONT, child, NULL, (void*)0));
test_assert(child == waitpid(child, &status, 0));
if (!(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP))
break;
}
}
if (use_traceexec)
test_assert(status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXEC << 8)));
else
test_assert(status == ((SIGTRAP << 8) | 0x7f));
ptrace_getregs(child, &regs);
#if !defined(__aarch64__)
// On aarch64, we may only ask this in a syscall-entry stop, which this is not
test_assert(SYS_execve == regs.ORIG_SYSCALLNO);
#endif
if (!use_traceexec)
test_assert(0 == regs.SYSCALL_RESULT);
/* Check that we have actually transitioned */
test_assert(proc_num_args(child) == 2);
test_assert(0 == ptrace(PTRACE_CONT, child, NULL, (void*)0));
test_assert(child == waitpid(child, &status, 0));
test_assert(WIFEXITED(status) && WEXITSTATUS(status) == 77);
}
int main(int argc, char** argv) {
test_assert(proc_num_args(getpid()) == argc);
if (argc == 2) {
return 77;
}
/* Test that PTRACE_ATTACH produces a raw SIGTRAP after exiting exec, when
PTRACE_O_TRACEEXEC is not used. */
run_test(argv, 0, 0);
/* Test that rr doesn't mind exec stops and syscall exit stops both happening */
run_test(argv, 1, 1);
atomic_puts("EXIT-SUCCESS");
return 0;
}