about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--doc/hacking.md22
-rw-r--r--lib/chaseccomp/buffer.chasc2
-rw-r--r--lib/chaseccomp/chaseccomp.c37
-rw-r--r--lib/chaseccomp/common.chasc8
-rwxr-xr-xlib/chaseccomp/gen_defs6
-rwxr-xr-xlib/chaseccomp/gen_syscalls36
-rw-r--r--lib/chaseccomp/network.chasc2
-rw-r--r--src/utils/sandbox.nim3
8 files changed, 80 insertions, 36 deletions
diff --git a/doc/hacking.md b/doc/hacking.md
index 99404064..12672fd1 100644
--- a/doc/hacking.md
+++ b/doc/hacking.md
@@ -212,18 +212,16 @@ Use `config.color.toml` and `my-test-case.color.expected` to preserve colors.
 
 ### Sandbox violations
 
-First, note that a nil deref can also trigger a sandbox violation. Read
-the stack trace to make sure you aren't dealing with that.
-
-Then, figure out if it's happening in a CGI process or a buffer
-process. If your buffer was swapped out for the console, it's likely the
-latter; otherwise, the former.
-
-Now change the appropriate sandbox handler from `SCMP_ACT_TRAP` to
-`SCMP_ACT_KILL_PROCESS`. Run `strace -f ./cha -o start.console-buffer [...] 2>a`,
-trigger the crash, then search for "killed by SIGSYS" in `a`. Copy the
-logged PID, then search backwards once; you should now see the syscall
-that got your process killed.
+You got a syscall number (assuming you're on Linux); look it up in the
+Linux syscall table for your architecture.
+
+To get more context on what happened, you can run
+`strace -f ./cha -o start.console-buffer [...] 2>a`, trigger the crash,
+then search for the last occurrence of "--- SIGSYS". Then search
+backwards on the PID to see the last syscalls.
+
+(Incidentally, strace also shows the syscall name, so it may be easier
+to check like that than looking it up in the syscall table.)
 
 ## Resources
 
diff --git a/lib/chaseccomp/buffer.chasc b/lib/chaseccomp/buffer.chasc
index 0a4aa555..0d14883f 100644
--- a/lib/chaseccomp/buffer.chasc
+++ b/lib/chaseccomp/buffer.chasc
@@ -33,6 +33,8 @@ ifeqdef SYS_sigreturn allow	# called by signal trampoline
 
 : deny
 ret trap
+: kill
+ret kill
 : eperm
 ret errno EPERM
 : allow
diff --git a/lib/chaseccomp/chaseccomp.c b/lib/chaseccomp/chaseccomp.c
index 9001b973..29e39a6b 100644
--- a/lib/chaseccomp/chaseccomp.c
+++ b/lib/chaseccomp/chaseccomp.c
@@ -16,16 +16,35 @@
 #include <sys/un.h>
 #include <fcntl.h>
 #include <stdint.h>
+#include <signal.h>
 
 #include "chaseccomp.h"
 
+static void sigsys_handler_buffer(int sig, siginfo_t *info, void *ucontext)
+{
+	fprintf(stderr, "Sandbox violation in buffer: syscall #%d\n",
+		info->si_syscall);
+	abort();
+}
+
 int cha_enter_buffer_sandbox(void)
 {
 	struct sock_filter filter[] = {
 #include "chasc_buffer.h"
 	};
+#ifndef EXPECTED_COUNT
+#error "network sandbox not built"
+#elsif EXPECTED_COUNT != COUNTOF(filter)
+#error "wrong network sandbox length"
+#endif
 	struct sock_fprog prog = { .len = COUNTOF(filter), .filter = filter };
+	struct sigaction act = {
+		.sa_flags = SA_SIGINFO,
+		.sa_sigaction = sigsys_handler_buffer,
+	};
 
+	if (sigaction(SIGSYS, &act, NULL) < 0)
+		return 0;
 	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
 		return 0;
 	if (syscall(SYS_seccomp, SECCOMP_SET_MODE_FILTER, 0, &prog))
@@ -33,13 +52,31 @@ int cha_enter_buffer_sandbox(void)
 	return 1;
 }
 
+static void sigsys_handler_network(int sig, siginfo_t *info, void *ucontext)
+{
+	fprintf(stderr, "Sandbox violation in network: syscall #%d\n",
+		info->si_syscall);
+}
+
 int cha_enter_network_sandbox(void)
 {
+#undef EXPECTED_COUNT
 	struct sock_filter filter[] = {
 #include "chasc_network.h"
 	};
+#ifndef EXPECTED_COUNT
+#error "network sandbox not built"
+#elsif EXPECTED_COUNT != COUNTOF(filter)
+#error "wrong network sandbox length"
+#endif
 	struct sock_fprog prog = { .len = COUNTOF(filter), .filter = filter };
+	struct sigaction act = {
+		.sa_flags = SA_SIGINFO,
+		.sa_sigaction = sigsys_handler_network,
+	};
 
+	if (sigaction(SIGSYS, &act, NULL) < 0)
+		return 0;
 	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
 		return 0;
 	if (syscall(SYS_seccomp, SECCOMP_SET_MODE_FILTER, 0, &prog))
diff --git a/lib/chaseccomp/common.chasc b/lib/chaseccomp/common.chasc
index 37f886dc..15c40053 100644
--- a/lib/chaseccomp/common.chasc
+++ b/lib/chaseccomp/common.chasc
@@ -22,6 +22,7 @@ ifeqdef SYS_brk allow
 # less common socket/file i/o
 ifeqdef SYS_lseek allow
 ifeqdef SYS_close allow
+ifeqdef SYS_shutdown allow
 
 # prevent glibc from getting our process murdered
 ifeqdef SYS_fstat eperm
@@ -48,6 +49,13 @@ ifeqdef SYS_exit_group allow	# for quit
 ifeqdef SYS_restart_syscall allow # for resuming poll on SIGCONT
 ifeqdef SYS_getrandom allow	# glibc calls it when initializing its malloc
 
+# so we don't trap on crash
+ifeqdef SYS_sigaction eperm
+ifeqdef SYS_rt_sigaction eperm
+ifeqdef SYS_rt_sigprocmask eperm
+ifeqdef SYS_gettid eperm
+ifeqdef SYS_tgkill kill
+
 # bionic-specific stuff
 ifdef __BIONIC__
 ifeqdef SYS_rt_sigprocmap allow
diff --git a/lib/chaseccomp/gen_defs b/lib/chaseccomp/gen_defs
index f4932b71..6fdedac0 100755
--- a/lib/chaseccomp/gen_defs
+++ b/lib/chaseccomp/gen_defs
@@ -46,7 +46,7 @@ EOF
 
 f() {
 	while read -r line
-	do	case $line in
+	do	case "$line" in
 		'include '*)	<"${line#* }" f ;;
 		*)		printf '%s\n' "$line" ;;
 		esac
@@ -55,7 +55,9 @@ f() {
 
 f | while read -r line
 do	line=${line%%#*}
-	case $line in
+	line=${line#"${line%%[![:space:]]*}"}
+	line=${line%"${line##*[![:space:]]}"}
+	case "$line" in
 	'')	;;
 	if??def' '*)
 		inst=${line%%def*}
diff --git a/lib/chaseccomp/gen_syscalls b/lib/chaseccomp/gen_syscalls
index a4d7d5b5..401403d5 100755
--- a/lib/chaseccomp/gen_syscalls
+++ b/lib/chaseccomp/gen_syscalls
@@ -9,7 +9,7 @@ test "$1" || die "usage: gen_syscalls [file]"
 
 find_label() {
 	printf '%s\n' "$labels" | while read -r line
-	do	case $line in
+	do	case "$line" in
 		"$1 "*)	printf '%d\n' "${line#* }"
 			break
 			;;
@@ -24,23 +24,19 @@ line_cut_next() {
 	if test "$line" = "$oline"; then line= ; fi
 }
 
-cut_label() {
+put_cmp() {
+	line_cut_next
+	val=$next
 	line_cut_next
 	label=$(find_label "$next")
 	test "$label" || die "missing label $next"
 	label=$(($label - 1 - $ip))
-}
-
-put_cmp() {
-	line_cut_next
-	val=$next
-	cut_label
-	printf '\t%s(%s, %d),\n' "$inst" "$val" "$label"
+	printf '\t%s(%s, %d),\n' "$1" "$val" "$label"
 }
 
 put_load() {
 	line_cut_next
-	printf '\t%s(%s),\n' "$inst" "$next"
+	printf '\tCHA_BPF_LOAD(%s),\n' "$next"
 }
 
 put_ret() {
@@ -52,13 +48,13 @@ put_ret() {
 	errno)	val="SECCOMP_RET_ERRNO | ($line & SECCOMP_RET_DATA)" ;;
 	*)	die "wrong retval $line" ;;
 	esac
-	printf '\t%s(%s),\n' "$inst" "$val"
+	printf '\tCHA_BPF_RET(%s),\n' "$val"
 }
 
 ip=0
 while read -r line
 do	line=${line%%#*}
-	case $line in
+	case "$line" in
 	''|define' '*) ;;
 	': '*)	line_cut_next
 		if test -n "$labels"
@@ -76,13 +72,15 @@ while read -r line
 do	line_cut_next
 	case $next in
 	:)	continue ;;
-	define)	printf '#%s %s\n' "$next $line" ; continue ;;
-	ret)	inst=CHA_BPF_RET put_ret ;;
-	ifeq)	inst=CHA_BPF_JE put_cmp ;;
-	ifne)	inst=CHA_BPF_JNE put_cmp ;;
-	ifle)	inst=CHA_BPF_JLE put_cmp ;;
-	load)	inst=CHA_BPF_LOAD put_load ;;
-	*)	die "unexpected instruction $inst" ;;
+	define)	printf '#%s %s\n' "$next" "$line" ; continue ;;
+	ret)	put_ret ;;
+	ifeq)	put_cmp CHA_BPF_JE;;
+	ifne)	put_cmp CHA_BPF_JNE;;
+	ifle)	put_cmp CHA_BPF_JLE;;
+	load)	put_load;;
+	*)	die "unexpected instruction $next" ;;
 	esac
 	ip=$(($ip + 1))
 done < "$1"
+
+printf '#define EXPECTED_COUNT %d\n' "$ip"
diff --git a/lib/chaseccomp/network.chasc b/lib/chaseccomp/network.chasc
index 8e7468d1..d6f86165 100644
--- a/lib/chaseccomp/network.chasc
+++ b/lib/chaseccomp/network.chasc
@@ -14,6 +14,8 @@ ifeqdef SYS_sendmsg allow
 ifeqdef SYS_getpid allow
 
 : deny
+ret trap
+: kill
 ret kill
 : eperm
 ret errno EPERM
diff --git a/src/utils/sandbox.nim b/src/utils/sandbox.nim
index d5c969de..b2e2c8b0 100644
--- a/src/utils/sandbox.nim
+++ b/src/utils/sandbox.nim
@@ -92,9 +92,6 @@ elif SandboxMode == stSeccomp:
   proc cha_enter_network_sandbox(): cint {.importc, cdecl.}
 
   proc enterBufferSandbox*(sockPath: string) =
-    onSignal SIGSYS:
-      discard sig
-      raise newException(Defect, "Sandbox violation in buffer")
     doAssert cha_enter_buffer_sandbox() == 1
 
   proc enterNetworkSandbox*() =