Skip to content

Commit

Permalink
Support for seccomp
Browse files Browse the repository at this point in the history
Remove PROOT_NO_SECCOMP=1 from travis

Closes: #106
Closes: #130
  • Loading branch information
jorge-lip authored and oxr463 committed May 14, 2019
1 parent 9045962 commit b48f633
Show file tree
Hide file tree
Showing 3 changed files with 267 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ before_install:
script:
- make -C src loader.exe loader-m32.exe build.h
- env CFLAGS=--coverage LDFLAGS='--coverage' make -C src proot care
- env PATH=/bin:/usr/bin:/sbin:/usr/sbin:$PWD/src PROOT_NO_SECCOMP=1 make -C tests
- env PATH=/bin:/usr/bin:/sbin:/usr/sbin:$PWD/src make -C tests

266 changes: 265 additions & 1 deletion src/tracee/event.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
* 02110-1301 USA.
*/

#include <stdio.h>
#include <sched.h> /* CLONE_*, */
#include <sys/types.h> /* pid_t, */
#include <sys/ptrace.h> /* ptrace(1), PTRACE_*, */
Expand Down Expand Up @@ -48,6 +49,7 @@
#include "attribute.h"
#include "compat.h"


/**
* Start @tracee->exe with the given @argv[]. This function
* returns -errno if an error occurred, otherwise 0.
Expand Down Expand Up @@ -204,6 +206,29 @@ static void print_talloc_hierarchy(int signum, siginfo_t *siginfo UNUSED, void *

static int last_exit_status = -1;

/**
* Check if kernel >= 4.8
*/
bool is_kernel_4_8(void) {
static int version_48 = -1;
static int major = 0;
static int minor = 0;

if (version_48 != -1)
return version_48;

version_48 = false;
struct utsname utsname;
if (uname(&utsname) < 0)
return false;

sscanf(utsname.release, "%d.%d", &major, &minor);
if (major >= 4)
if (minor >= 8)
version_48 = true;
return version_48;
}

/**
* Check if this instance of PRoot can *technically* handle @tracee.
*/
Expand Down Expand Up @@ -253,6 +278,9 @@ int event_loop()
struct sigaction signal_action;
long status;
int signum;
int kernel_4_8;

kernel_4_8 = is_kernel_4_8();

/* Kill all tracees when exiting. */
status = atexit(kill_all_tracees);
Expand Down Expand Up @@ -346,14 +374,249 @@ int event_loop()
continue;
}

signal = handle_tracee_event(tracee, tracee_status);
if (kernel_4_8) {
signal = handle_tracee_event_kernel_4_8(tracee, tracee_status);
}
else {
signal = handle_tracee_event(tracee, tracee_status);
}
(void) restart_tracee(tracee, signal);
}

return last_exit_status;
}

/**
* For kernels >= 4.8.0
* Handle the current event (@tracee_status) of the given @tracee.
* This function returns the "computed" signal that should be used to
* restart the given @tracee.
*/
int handle_tracee_event_kernel_4_8(Tracee *tracee, int tracee_status)
{
static bool seccomp_detected = false;
static bool seccomp_enabled = false; /* added for 4.8.0 */
long status;
int signal;

/* Don't overwrite restart_how if it is explicitly set
* elsewhere, i.e in the ptrace emulation when single
* stepping. */
if (tracee->restart_how == 0) {
/* When seccomp is enabled, all events are restarted in
* non-stop mode, but this default choice could be overwritten
* later if necessary. The check against "sysexit_pending"
* ensures PTRACE_SYSCALL (used to hit the exit stage under
* seccomp) is not cleared due to an event that would happen
* before the exit stage, eg. PTRACE_EVENT_EXEC for the exit
* stage of execve(2). */
if (tracee->seccomp == ENABLED && !tracee->sysexit_pending)
tracee->restart_how = PTRACE_CONT;
else
tracee->restart_how = PTRACE_SYSCALL;
}

/* Not a signal-stop by default. */
signal = 0;

if (WIFEXITED(tracee_status)) {
last_exit_status = WEXITSTATUS(tracee_status);
VERBOSE(tracee, 1,
"vpid %" PRIu64 ": exited with status %d",
tracee->vpid, last_exit_status);
terminate_tracee(tracee);
}
else if (WIFSIGNALED(tracee_status)) {
check_architecture(tracee);
VERBOSE(tracee, (int) (last_exit_status != -1),
"vpid %" PRIu64 ": terminated with signal %d",
tracee->vpid, WTERMSIG(tracee_status));
terminate_tracee(tracee);
}
else if (WIFSTOPPED(tracee_status)) {
/* Don't use WSTOPSIG() to extract the signal
* since it clears the PTRACE_EVENT_* bits. */
signal = (tracee_status & 0xfff00) >> 8;

switch (signal) {
static bool deliver_sigtrap = false;

case SIGTRAP: {
const unsigned long default_ptrace_options = (
PTRACE_O_TRACESYSGOOD |
PTRACE_O_TRACEFORK |
PTRACE_O_TRACEVFORK |
PTRACE_O_TRACEVFORKDONE |
PTRACE_O_TRACEEXEC |
PTRACE_O_TRACECLONE |
PTRACE_O_TRACEEXIT);

/* Distinguish some events from others and
* automatically trace each new process with
* the same options.
*
* Note that only the first bare SIGTRAP is
* related to the tracing loop, others SIGTRAP
* carry tracing information because of
* TRACE*FORK/CLONE/EXEC. */
if (deliver_sigtrap)
break; /* Deliver this signal as-is. */

deliver_sigtrap = true;

/* Try to enable seccomp mode 2... */
status = ptrace(PTRACE_SETOPTIONS, tracee->pid, NULL,
default_ptrace_options | PTRACE_O_TRACESECCOMP);
if (status < 0) {
seccomp_enabled = false;
/* ... otherwise use default options only. */
status = ptrace(PTRACE_SETOPTIONS, tracee->pid, NULL,
default_ptrace_options);
if (status < 0) {
note(tracee, ERROR, SYSTEM, "ptrace(PTRACE_SETOPTIONS)");
exit(EXIT_FAILURE);
}
}
else {
if (getenv("PROOT_NO_SECCOMP") == NULL)
seccomp_enabled = true;
}
}
/* Fall through. */
case SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8:
case SIGTRAP | PTRACE_EVENT_SECCOMP << 8:

if (!seccomp_detected && seccomp_enabled) {
VERBOSE(tracee, 1, "ptrace acceleration (seccomp mode 2) enabled");
tracee->seccomp = ENABLED;
seccomp_detected = true;
}

if (signal == (SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8) ||
signal == (SIGTRAP | PTRACE_EVENT_SECCOMP << 8)) {

unsigned long flags = 0;
signal = 0;

/* SECCOMP TRAP can only be received for
* sysenter events, ignore otherwise */
if (!IS_IN_SYSENTER(tracee)) {
tracee->restart_how = PTRACE_CONT;
return 0;
}
status = ptrace(PTRACE_GETEVENTMSG, tracee->pid, NULL, &flags);
if (status < 0)
break;

if (tracee->seccomp == ENABLED && (flags & FILTER_SYSEXIT) == 0) {
tracee->restart_how = PTRACE_CONT;
translate_syscall(tracee);

if (tracee->seccomp == DISABLING)
tracee->restart_how = PTRACE_SYSCALL;
break;
}
}

/* Fall through. */
case SIGTRAP | 0x80:

signal = 0;

/* This tracee got signaled then freed during the
sysenter stage but the kernel reports the sysexit
stage; just discard this spurious tracee/event. */

if (tracee->exe == NULL) {
tracee->restart_how = PTRACE_CONT; /* SYSCALL OR CONT */
return 0;
}

switch (tracee->seccomp) {
case ENABLED:
if (IS_IN_SYSENTER(tracee)) {
/* sysenter: ensure the sysexit
* stage will be hit under seccomp. */
tracee->restart_how = PTRACE_SYSCALL;
tracee->sysexit_pending = true;
}
else {
/* sysexit: the next sysenter
* will be notified by seccomp. */
tracee->restart_how = PTRACE_CONT;
tracee->sysexit_pending = false;
}
/* Fall through. */
case DISABLED:
translate_syscall(tracee);

/* This syscall has disabled seccomp. */
if (tracee->seccomp == DISABLING) {
tracee->restart_how = PTRACE_SYSCALL;
tracee->seccomp = DISABLED;
}

break;

case DISABLING:
/* Seccomp was disabled by the
* previous syscall, but its sysenter
* stage was already handled. */
tracee->seccomp = DISABLED;
if (IS_IN_SYSENTER(tracee))
tracee->status = 1;
break;
}
break;

case SIGTRAP | PTRACE_EVENT_VFORK << 8:
signal = 0;
(void) new_child(tracee, CLONE_VFORK);
break;

case SIGTRAP | PTRACE_EVENT_FORK << 8:
case SIGTRAP | PTRACE_EVENT_CLONE << 8:
signal = 0;
(void) new_child(tracee, 0);
break;

case SIGTRAP | PTRACE_EVENT_VFORK_DONE << 8:
case SIGTRAP | PTRACE_EVENT_EXEC << 8:
case SIGTRAP | PTRACE_EVENT_EXIT << 8:
signal = 0;
break;

case SIGSTOP:
/* Stop this tracee until PRoot has received
* the EVENT_*FORK|CLONE notification. */
if (tracee->exe == NULL) {
tracee->sigstop = SIGSTOP_PENDING;
signal = -1;
}

/* For each tracee, the first SIGSTOP
* is only used to notify the tracer. */
if (tracee->sigstop == SIGSTOP_IGNORED) {
tracee->sigstop = SIGSTOP_ALLOWED;
signal = 0;
}
break;

default:
/* Deliver this signal as-is. */
break;
}
}

/* Clear the pending event, if any. */
tracee->as_ptracee.event4.proot.pending = false;

return signal;
}


/**
* For kernels < 4.8.0
* Handle the current event (@tracee_status) of the given @tracee.
* This function returns the "computed" signal that should be used to
* restart the given @tracee.
Expand Down Expand Up @@ -577,6 +840,7 @@ int handle_tracee_event(Tracee *tracee, int tracee_status)
return signal;
}


/**
* Restart the given @tracee with the specified @signal. This
* function returns false if the tracee was not restarted (error or
Expand Down
1 change: 1 addition & 0 deletions src/tracee/event.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
extern int launch_process(Tracee *tracee, char *const argv[]);
extern int event_loop();
extern int handle_tracee_event(Tracee *tracee, int tracee_status);
extern int handle_tracee_event_kernel_4_8(Tracee *tracee, int tracee_status);
extern bool restart_tracee(Tracee *tracee, int signal);

#endif /* TRACEE_EVENT_H */

0 comments on commit b48f633

Please sign in to comment.