Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes to seccomp event handling logic #266

Merged
merged 5 commits into from
Sep 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 37 additions & 77 deletions src/tracee/event.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,14 +212,14 @@ static int last_exit_status = -1;
*/
bool is_kernel_4_8(void)
{
static int version_48 = -1;
static int major = 0;
static int minor = 0;
static int version_48 = -1;
int major = 0;
int minor = 0;

if (version_48 != -1)
return version_48;

version_48 = false;
version_48 = false;

struct utsname utsname;

Expand All @@ -228,8 +228,8 @@ bool is_kernel_4_8(void)

sscanf(utsname.release, "%d.%d", &major, &minor);

if (major >= 4 && minor >= 8)
version_48 = true;
if ((major == 4 && minor >= 8) || major > 4)
version_48 = true;

return version_48;
}
Expand Down Expand Up @@ -379,11 +379,11 @@ int event_loop()
continue;
}

if (kernel_4_8) {
signal = handle_tracee_event_kernel_4_8(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);
signal = handle_tracee_event(tracee, tracee_status);
}
(void) restart_tracee(tracee, signal);
}
Expand Down Expand Up @@ -491,37 +491,45 @@ int handle_tracee_event_kernel_4_8(Tracee *tracee, int tracee_status)
case SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8:
case SIGTRAP | PTRACE_EVENT_SECCOMP << 8:

if (!seccomp_detected && seccomp_enabled) {
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)) {
signal == (SIGTRAP | PTRACE_EVENT_SECCOMP << 8)) {

unsigned long flags = 0;
signal = 0;


status = ptrace(PTRACE_GETEVENTMSG, tracee->pid, NULL, &flags);
if (status < 0)
break;

/* SECCOMP TRAP can only be received for
* sysenter events, ignore otherwise */
* sysenter events. It is sometimes possible for sysenter
* to be handled at the normal PTRACE_SYSCALL SIGTRAP handler,
* before seccomp trap arrives.
* This may happen for example during handling of the first
* syscall the traced process makes, before seccomp is enabled,
* however there is some other random and unknown factor that affects that.
* If this happened, then continue until the next syscall
* or sysexit if necessary. */
if (!IS_IN_SYSENTER(tracee)) {
tracee->restart_how = (flags & FILTER_SYSEXIT) ? PTRACE_SYSCALL : PTRACE_CONT;
break;
}

if (tracee->seccomp == ENABLED && (flags & FILTER_SYSEXIT) == 0) {
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;
}
}
translate_syscall(tracee);

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

/* Fall through. */
case SIGTRAP | 0x80:
Expand All @@ -531,7 +539,7 @@ int handle_tracee_event_kernel_4_8(Tracee *tracee, int tracee_status)
/* 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;
Expand Down Expand Up @@ -629,7 +637,6 @@ int handle_tracee_event_kernel_4_8(Tracee *tracee, int tracee_status)
int handle_tracee_event(Tracee *tracee, int tracee_status)
{
static bool seccomp_detected = false;
static bool seccomp_enabled = false;
long status;
int signal;

Expand Down Expand Up @@ -702,7 +709,6 @@ int handle_tracee_event(Tracee *tracee, int tracee_status)
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);
Expand All @@ -711,50 +717,8 @@ int handle_tracee_event(Tracee *tracee, int tracee_status)
exit(EXIT_FAILURE);
}
}
else {
if (getenv("PROOT_NO_SECCOMP") == NULL)
seccomp_enabled = true;
}
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)

/* 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;
}
}
#endif
/* Fall through. */
case SIGTRAP | 0x80:
signal = 0;
Expand Down Expand Up @@ -804,8 +768,6 @@ int handle_tracee_event(Tracee *tracee, int tracee_status)
}
break;

#if LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0)

case SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8:
case SIGTRAP | PTRACE_EVENT_SECCOMP << 8: {
unsigned long flags = 0;
Expand Down Expand Up @@ -847,8 +809,6 @@ int handle_tracee_event(Tracee *tracee, int tracee_status)
break;
}

#endif

case SIGTRAP | PTRACE_EVENT_VFORK << 8:
signal = 0;
(void) new_child(tracee, CLONE_VFORK);
Expand Down
3 changes: 3 additions & 0 deletions test/GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ check-%.c: $(ROOTFS)/bin/% setup
$(call check_c,$*,$(PROOT) -b /proc -r $(ROOTFS) /bin/$*)

# Special cases.
check-test-sysexit.c: test-sysexit
$(call check_c,$<,env PROOT_FORCE_KOMPAT=1 $(PROOT) -k 3.4242XX ./$<)

check-test-bdc90417.c: test-bdc90417
$(call check_c,$<,$(PROOT) -w . ./$<)

Expand Down
35 changes: 35 additions & 0 deletions test/test-sysexit.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- */
#include <stdlib.h>
#include <sys/utsname.h>
#include <sys/wait.h> /* wait(2), */
#include <unistd.h>
#include <string.h>

/* Related to github issue #106.
* Test if sysexit handlers execute, using uname handling
* in the kompat extension. The test case is meant to be
* run with "-k 3.4242XX" cmdline argument.
* The bug could occur during the first traced syscall,
* before seccomp got enabled.
* However there was some kind of a random factor there,
* so we fork out many processes to make it likely that at least
* some would hit this problem. */

int main()
{
int status;
struct utsname s;
for (int i = 0; i < 5; i++) {
fork();
}
uname(&s);
int child_status;
while ((status = wait(&child_status)) >= 0) {
if (!WIFEXITED(child_status) || (WEXITSTATUS(child_status) == EXIT_FAILURE))
exit(EXIT_FAILURE);
}
if (strcmp("3.4242XX", s.release) == 0) {
exit(EXIT_SUCCESS);
}
exit(EXIT_FAILURE);
}