mirror of
https://github.com/proot-me/proot.git
synced 2025-08-28 16:43:49 +02:00
The event handler for the old kernel may still be called on new kernels. This causes issues since the two event handlers maintains their own global states unaware of each other. In particular, execve+ptrace handling from the loader of the tracee will issue an `execve(0x1, ...)` to signal proot of the start addresses. This triggers a `SIGTRAP` to the tracee for the tracer to handle. However, the event handler expect one initial `SIGTRAP` to have special meaning and if the wrong event handler is called, it will incorrectly assume this `SIGTRAP` is the special one and acts incorrectly. (In this case, causing the signaling `execve` to run again and set the addresses incorrectly.)
80 lines
1.7 KiB
C
80 lines
1.7 KiB
C
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/ptrace.h>
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <errno.h> /* errno(3), */
|
|
#include <sys/user.h> /* struct user*, */
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
enum __ptrace_request restart_how;
|
|
int last_exit_status = -1;
|
|
int child_status;
|
|
pid_t *pids = NULL;
|
|
long status;
|
|
int signal;
|
|
pid_t pid;
|
|
|
|
if (argc <= 1) {
|
|
fprintf(stderr, "Usage: %s /path/to/exe [args]\n", argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
pid = fork();
|
|
switch(pid) {
|
|
case -1:
|
|
perror("fork()");
|
|
exit(EXIT_FAILURE);
|
|
|
|
case 0: /* child */
|
|
status = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
|
|
if (status < 0) {
|
|
perror("ptrace(TRACEME)");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* Synchronize with the tracer's event loop. */
|
|
kill(getpid(), SIGSTOP);
|
|
|
|
execvp(argv[1], &argv[1]);
|
|
exit(EXIT_FAILURE);
|
|
|
|
default: /* parent */
|
|
break;
|
|
}
|
|
|
|
while (1) {
|
|
int tracee_status;
|
|
pid = waitpid(-1, &tracee_status, __WALL);
|
|
if (pid < 0) {
|
|
perror("waitpid()");
|
|
if (errno != ECHILD)
|
|
exit(EXIT_FAILURE);
|
|
break;
|
|
}
|
|
|
|
if (WIFEXITED(tracee_status)) {
|
|
fprintf(stderr, "pid %d: exited with status %d\n",
|
|
pid, WEXITSTATUS(tracee_status));
|
|
last_exit_status = WEXITSTATUS(tracee_status);
|
|
continue; /* Skip the call to ptrace(SYSCALL). */
|
|
}
|
|
else if (WIFSTOPPED(tracee_status)) {
|
|
int signal = tracee_status >> 8;
|
|
if (signal != SIGTRAP && signal != SIGSTOP) {
|
|
// We expect a SIGSTOP since the child sends it to itself
|
|
// and a SIGTRAP from the exec + ptrace.
|
|
// Anything else is an error.
|
|
fprintf(stderr, "Unexpected signal recieved from pid: %d.\n",
|
|
pid);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
status = ptrace(PTRACE_CONT, pid, NULL, 0);
|
|
}
|
|
}
|
|
|
|
return last_exit_status;
|
|
}
|