summaryrefslogtreecommitdiffstats
path: root/sandbox/linux/seccomp/sandbox.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sandbox/linux/seccomp/sandbox.cc')
-rw-r--r--sandbox/linux/seccomp/sandbox.cc219
1 files changed, 190 insertions, 29 deletions
diff --git a/sandbox/linux/seccomp/sandbox.cc b/sandbox/linux/seccomp/sandbox.cc
index 32480b3..0b09457 100644
--- a/sandbox/linux/seccomp/sandbox.cc
+++ b/sandbox/linux/seccomp/sandbox.cc
@@ -9,13 +9,14 @@
namespace playground {
// Global variables
-int Sandbox::proc_self_maps_ = -1;
-enum Sandbox::SandboxStatus Sandbox::status_ = STATUS_UNKNOWN;
-int Sandbox::pid_;
-int Sandbox::processFdPub_;
-int Sandbox::cloneFdPub_;
-Sandbox::ProtectedMap Sandbox::protectedMap_;
-std::vector<SecureMem::Args*> Sandbox::secureMemPool_;
+int Sandbox::proc_self_maps_ = -1;
+enum Sandbox::SandboxStatus Sandbox::status_ = STATUS_UNKNOWN;
+int Sandbox::pid_;
+int Sandbox::processFdPub_;
+int Sandbox::cloneFdPub_;
+Sandbox::SysCalls::kernel_sigaction Sandbox::sa_segv_;
+Sandbox::ProtectedMap Sandbox::protectedMap_;
+std::vector<SecureMem::Args*> Sandbox::secureMemPool_;
bool Sandbox::sendFd(int transport, int fd0, int fd1, const void* buf,
size_t len) {
@@ -120,10 +121,11 @@ void Sandbox::setupSignalHandlers() {
sys.sigaction(SIGCHLD, &sa, NULL);
// Set up SEGV handler for dealing with RDTSC instructions, system calls
- // that have been rewritten to use INT0, and for sigpending() emulation.
+ // that have been rewritten to use INT0, for sigprocmask() emulation, for
+ // the creation of threads, and for user-provided SEGV handlers.
sa.sa_sigaction_ = segv();
- sa.sa_flags = SA_SIGINFO;
- sys.sigaction(SIGSEGV, &sa, NULL);
+ sa.sa_flags = SA_SIGINFO | SA_NODEFER;
+ sys.sigaction(SIGSEGV, &sa, &sa_segv_);
// Unblock SIGSEGV and SIGCHLD
SysCalls::kernel_sigset_t mask;
@@ -263,16 +265,77 @@ void (*Sandbox::segv())(int signo, SysCalls::siginfo *context, void *unused) {
"lea playground$syscallWrapper(%%rip), %%rcx\n"
"jmp *%%rcx\n"
- // This was a genuine segmentation fault. Trigger the kernel's default
- // signal disposition. The only way we can do this from seccomp mode
- // is by blocking the signal and retriggering it.
- "16:mov $2, %%edi\n" // stderr
- "lea 300f(%%rip), %%rsi\n" // "Segmentation fault\n"
- "mov $301f-300f, %%edx\n"
- "mov $1, %%eax\n" // NR_write
+ // In order to implement SA_NODEFER, we have to keep track of recursive
+ // calls to SIGSEGV handlers. This means we have to increment a counter
+ // before calling the user's signal handler, and decrement it on
+ // leaving the user's signal handler.
+ // Some signal handlers look at the return address of the signal
+ // stack, and more importantly "gdb" uses the call to rt_sigreturn()
+ // as a magic signature when doing stacktraces. So, we have to use
+ // a little more unusual code to regain control after the user's
+ // signal handler is done. We adjust the return address to point to
+ // non-executable memory. And when we trigger another SEGV we pop the
+ // extraneous signal frame and then call rt_sigreturn().
+ // N.B. We currently do not correctly adjust the SEGV counter, if the
+ // user's signal handler exits in way other than by returning (e.g. by
+ // directly calling rt_sigreturn(), or by calling siglongjmp()).
+ "16:lea 22f(%%rip), %%r14\n"
+ "cmp %%r14, %%r15\n"
+ "jnz 17f\n" // check if returning from user's handler
+ "decl %%gs:0x105C-0xE0\n" // decrement SEGV recursion counter
+ "mov 0xA8(%%rsp), %%rsp\n" // %rsp at time of segmentation fault
+ "mov $0xF, %%eax\n" // NR_rt_sigreturn
"syscall\n"
- "orb $4, 0x131(%%rsp)\n" // signal mask at time of segmentation fault
+
+ // This was a genuine segmentation fault. Check Sandbox::sa_segv_ for
+ // what we are supposed to do.
+ "17:mov playground$sa_segv@GOTPCREL(%%rip), %%rax\n"
+ "cmp $0, 0(%%rax)\n" // SIG_DFL
+ "jz 18f\n"
+ "cmp $1, 0(%%rax)\n" // SIG_IGN
+ "jnz 19f\n" // can't really ignore synchronous signals
+
+ // Trigger the kernel's default signal disposition. The only way we can
+ // do this from seccomp mode is by blocking the signal and retriggering
+ // it.
+ "18:orb $4, 0x131(%%rsp)\n" // signal mask at time of segmentation fault
"ret\n"
+
+ // Check sa_flags:
+ // - We can ignore SA_NOCLDSTOP, SA_NOCLDWAIT, and SA_RESTART as they
+ // do not have any effect for SIGSEGV.
+ // - On x86-64, we can also ignore SA_SIGINFO, as the calling
+ // conventions for sa_handler() are a subset of the conventions for
+ // sa_sigaction().
+ // - We have to always register our signal handler with SA_NODEFER so
+ // that the user's signal handler can make system calls which might
+ // require additional help from our SEGV handler.
+ // - If the user's signal handler wasn't supposed to be SA_NODEFER, then
+ // we emulate this behavior by keeping track of a recursion counter.
+ //
+ // TODO(markus): If/when we add support for sigaltstack(), we have to
+ // handle SA_ONSTACK.
+ "19:cmpl $0, %%gs:0x105C-0xE0\n"// check if we failed inside of SEGV handler
+ "jnz 18b\n" // if so, then terminate program
+ "mov 0(%%rax), %%rbx\n" // sa_segv_.sa_sigaction
+ "mov 8(%%rax), %%rcx\n" // sa_segv_.sa_flags
+ "btl $31, %%ecx\n" // SA_RESETHAND
+ "jnc 20f\n"
+ "movq $0, 0(%%rax)\n" // set handler to SIG_DFL
+ "20:btl $30, %%ecx\n" // SA_NODEFER
+ "jc 21f\n"
+ "mov %%r14, 0(%%rsp)\n" // trigger a SEGV on return, so that we can
+ "incl %%gs:0x105C-0xE0\n" // clean up state; incr. recursion counter
+ "21:jmp *%%rbx\n" // call user's signal handler
+
+
+ // Non-executable version of the restorer function. We use this to
+ // trigger a SEGV upon returning from the user's signal handler, giving
+ // us an ability to clean up prior to returning from the SEGV handler.
+ ".pushsection .data\n" // move code into non-executable section
+ "22:mov $0xF, %%rax\n" // gdb looks for this signature when doing
+ "syscall\n" // backtraces
+ ".popsection\n"
#elif defined(__i386__)
// Inspect instruction at the point where the segmentation fault
// happened. If it is RDTSC, forward the request to the trusted
@@ -441,16 +504,116 @@ void (*Sandbox::segv())(int signo, SysCalls::siginfo *context, void *unused) {
"19:call playground$syscallWrapper\n"
"jmp 3b\n"
- // This was a genuine segmentation fault. Trigger the kernel's default
- // signal disposition. The only way we can do this from seccomp mode
- // is by blocking the signal and retriggering it.
- "20:mov $2, %%ebx\n" // stderr
- "lea 300f, %%ecx\n" // "Segmentation fault\n"
- "mov $301f-300f, %%edx\n"
- "mov $4, %%eax\n" // NR_write
+ // In order to implement SA_NODEFER, we have to keep track of recursive
+ // calls to SIGSEGV handlers. This means we have to increment a counter
+ // before calling the user's signal handler, and decrement it on
+ // leaving the user's signal handler.
+ // Some signal handlers look at the return address of the signal
+ // stack, and more importantly "gdb" uses the call to {,rt_}sigreturn()
+ // as a magic signature when doing stacktraces. So, we have to use
+ // a little more unusual code to regain control after the user's
+ // signal handler is done. We adjust the return address to point to
+ // non-executable memory. And when we trigger another SEGV we pop the
+ // extraneous signal frame and then call sigreturn().
+ // N.B. We currently do not correctly adjust the SEGV counter, if the
+ // user's signal handler exits in way other than by returning (e.g. by
+ // directly calling {,rt_}sigreturn(), or by calling siglongjmp()).
+ "20:lea 30f, %%edi\n" // rt-style restorer function
+ "lea 31f, %%esi\n" // legacy restorer function
+ "cmp %%ebp, %%edi\n" // check if returning from user's handler
+ "jnz 21f\n"
+ "decl %%fs:0x1040-0x58\n" // decrement SEGV recursion counter
+ "mov 0xC0(%%esp), %%esp\n" // %esp at time of segmentation fault
+ "jmp 29f\n"
+ "21:cmp %%ebp, %%esi\n" // check if returning from user's handler
+ "jnz 22f\n"
+ "decl %%fs:0x1040-0x58\n" // decrement SEGV recursion counter
+ "mov 0xC0(%%esp), %%esp\n" // %esp at time of segmentation fault
+ "jmp 6b\n"
+
+ // This was a genuine segmentation fault. Check Sandbox::sa_segv_ for
+ // what we are supposed to do.
+ "22:lea playground$sa_segv, %%eax\n"
+ "cmp $0, 0(%%eax)\n" // SIG_DFL
+ "jz 23f\n"
+ "cmp $1, 0(%%eax)\n" // SIG_IGN
+ "jnz 24f\n" // can't really ignore synchronous signals
+
+ // Trigger the kernel's default signal disposition. The only way we can
+ // do this from seccomp mode is by blocking the signal and retriggering
+ // it.
+ "23:orb $4, 0xFD(%%esp)\n" // signal mask at time of segmentation fault
+ "jmp 5b\n"
+
+ // Check sa_flags:
+ // - We can ignore SA_NOCLDSTOP, SA_NOCLDWAIT, and SA_RESTART as they
+ // do not have any effect for SIGSEGV.
+ // - We have to always register our signal handler with SA_NODEFER so
+ // that the user's signal handler can make system calls which might
+ // require additional help from our SEGV handler.
+ // - If the user's signal handler wasn't supposed to be SA_NODEFER, then
+ // we emulate this behavior by keeping track of a recursion counter.
+ //
+ // TODO(markus): If/when we add support for sigaltstack(), we have to
+ // handle SA_ONSTACK.
+ "24:cmpl $0, %%fs:0x1040-0x58\n"// check if we failed inside of SEGV handler
+ "jnz 23b\n" // if so, then terminate program
+ "mov 0(%%eax), %%ebx\n" // sa_segv_.sa_sigaction
+ "mov 4(%%eax), %%ecx\n" // sa_segv_.sa_flags
+ "btl $31, %%ecx\n" // SA_RESETHAND
+ "jnc 25f\n"
+ "movl $0, 0(%%eax)\n" // set handler to SIG_DFL
+ "25:btl $30, %%ecx\n" // SA_NODEFER
+ "jc 28f\n"
+ "btl $2, %%ecx\n" // SA_SIGINFO
+ "jnc 26f\n"
+ "mov %%edi, 0(%%esp)\n" // trigger a SEGV on return
+ "incl %%fs:0x1040-0x58\n" // increment recursion counter
+ "jmp *%%ebx\n" // call user's signal handler
+ "26:mov %%esi, 0(%%esp)\n"
+ "incl %%fs:0x1040-0x58\n" // increment recursion counter
+
+ // We always register the signal handler to give us rt-style signal
+ // frames. But if the user asked for legacy signal frames, we must
+ // convert the signal frame prior to calling the user's signal handler.
+ "27:sub $0x1C8, %%esp\n" // a legacy signal stack is much larger
+ "mov 0x1CC(%%esp), %%eax\n" // push signal number
+ "push %%eax\n"
+ "mov 0x1CC(%%esp), %%eax\n" // push restorer function
+ "push %%eax\n"
+ "lea 0x274(%%esp), %%esi\n" // copy siginfo register values
+ "lea 0x8(%%esp), %%edi\n" // into new location
+ "mov $22, %%ecx\n"
+ "cld\n"
+ "rep movsl\n"
+ "mov 0x2CC(%%esp), %%eax\n" // copy first half of signal mask
+ "mov %%eax, 0x58(%%esp)\n"
+ "lea 31f, %%esi\n"
+ "lea 0x2D4(%%esp), %%edi\n" // patch up retcode magic numbers
+ "movb $2, %%cl\n"
+ "rep movsl\n"
+ "jmp *%%ebx\n" // call user's signal handler
+ "28:lea 6b, %%eax\n" // set appropriate restorer function
+ "mov %%eax, 0(%%esp)\n"
+ "btl $2, %%ecx\n" // SA_SIGINFO
+ "jnc 27b\n"
+ "lea 29f, %%eax\n"
+ "mov %%eax, 0(%%esp)\n" // set appropriate restorer function
+ "jmp *%%ebx\n" // call user's signal handler
+ "29:pushl $30f\n" // emulate rt_sigreturn()
+ "jmp 5b\n"
+
+ // Non-executable versions of the restorer function. We use these to
+ // trigger a SEGV upon returning from the user's signal handler, giving
+ // us an ability to clean up prior to returning from the SEGV handler.
+ ".pushsection .data\n" // move code into non-executable section
+ "30:mov $173, %%eax\n" // NR_rt_sigreturn
+ "int $0x80\n" // gdb looks for this signature when doing
+ ".byte 0\n" // backtraces
+ "31:pop %%eax\n"
+ "mov $119, %%eax\n" // NR_sigreturn
"int $0x80\n"
- "orb $4, 0xFD(%%esp)\n" // signal mask at time of segmentation fault
- "jmp 4b\n"
+ ".popsection\n"
#else
#error Unsupported target platform
#endif
@@ -459,8 +622,6 @@ void (*Sandbox::segv())(int signo, SysCalls::siginfo *context, void *unused) {
"100:.asciz \"RDTSC(P): Executing handler\\n\"\n"
"200:.asciz \"INT $0x0: Executing handler\\n\"\n"
#endif
- "300:.ascii \"Segmentation fault\\n\"\n"
- "301:\n"
".popsection\n"
"999:pop %0\n"
: "=g"(fnc)