blob: 4fbc106b90045fd9c3a2ba824e30731a76d85492 [file] [log] [blame]
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <asm/unistd.h>
.internal playground$segvSignalHandler
.global playground$segvSignalHandler
playground$segvSignalHandler:
// Inspect instruction at the point where the segmentation fault
// happened. If it is RDTSC, forward the request to the trusted
// thread.
mov $-3, %r14 // request for RDTSC
mov 0xB0(%rsp), %r15 // %rip at time of segmentation fault
cmpw $0x310F, (%r15) // RDTSC
jz 0f
cmpw $0x010F, (%r15) // RDTSCP
jnz 8f
cmpb $0xF9, 2(%r15)
jnz 8f
mov $-4, %r14 // request for RDTSCP
0:
#ifndef NDEBUG
lea 100f(%rip), %rdi
call playground$debugMessage
#endif
sub $4, %rsp
push %r14
mov %gs:16, %edi // fd = threadFdPub
mov %rsp, %rsi // buf = %rsp
mov $4, %edx // len = sizeof(int)
1:mov $1, %eax // NR_write
syscall
cmp %rax, %rdx
jz 5f
cmp $-4, %eax // EINTR
jz 1b
2:add $12, %rsp
movq $0, 0x98(%rsp) // %rax at time of segmentation fault
movq $0, 0x90(%rsp) // %rdx at time of segmentation fault
cmpw $0x310F, (%r15) // RDTSC
jz 3f
movq $0, 0xA0(%rsp) // %rcx at time of segmentation fault
3:addq $2, 0xB0(%rsp) // %rip at time of segmentation fault
cmpw $0x010F, (%r15) // RDTSC
jnz 4f
addq $1, 0xB0(%rsp) // %rip at time of segmentation fault
4:ret
5:mov $12, %edx // len = 3*sizeof(int)
6:mov $0, %eax // NR_read
syscall
cmp $-4, %eax // EINTR
jz 6b
cmp %rax, %rdx
jnz 2b
mov 0(%rsp), %eax
mov 4(%rsp), %edx
mov 8(%rsp), %ecx
add $12, %rsp
mov %rdx, 0x90(%rsp) // %rdx at time of segmentation fault
cmpw $0x310F, (%r15) // RDTSC
jz 7f
mov %rcx, 0xA0(%rsp) // %rcx at time of segmentation fault
7:mov %rax, 0x98(%rsp) // %rax at time of segmentation fault
jmp 3b
// If the instruction is INT 0, then this was probably the result
// of playground::Library being unable to find a way to safely
// rewrite the system call instruction. Retrieve the CPU register
// at the time of the segmentation fault and invoke
// syscallEntryPointWithFrame().
8:cmpw $0x00CD, (%r15) // INT $0x0
jnz 16f
cmpq $__NR_clone + 0xF001, 0x98(%rsp)
jz .L_handle_callback_request
#ifndef NDEBUG
lea 200f(%rip), %rdi
call playground$debugMessage
#endif
mov 0x98(%rsp), %rax // %rax at time of segmentation fault
mov 0x70(%rsp), %rdi // %rdi at time of segmentation fault
mov 0x78(%rsp), %rsi // %rsi at time of segmentation fault
mov 0x90(%rsp), %rdx // %rdx at time of segmentation fault
mov 0x40(%rsp), %r10 // %r10 at time of segmentation fault
mov 0x30(%rsp), %r8 // %r8 at time of segmentation fault
mov 0x38(%rsp), %r9 // %r9 at time of segmentation fault
// Handle rt_sigprocmask()
cmp $14, %rax // NR_rt_sigprocmask
jnz 12f
mov $-22, %rax // -EINVAL
cmp $8, %r10 // %r10 = sigsetsize (8 bytes = 64 signals)
jl 7b
mov 0x130(%rsp), %r10 // signal mask at time of segmentation fault
test %rsi, %rsi // only set mask, if set is non-NULL
jz 11f
mov 0(%rsi), %rsi
cmp $0, %rdi // %rdi = how (SIG_BLOCK)
jnz 9f
or %rsi, 0x130(%rsp) // signal mask at time of segmentation fault
jmp 11f
9:cmp $1, %rdi // %rdi = how (SIG_UNBLOCK)
jnz 10f
xor $-1, %rsi
and %rsi, 0x130(%rsp) // signal mask at time of segmentation fault
jmp 11f
10:cmp $2, %rdi // %rdi = how (SIG_SETMASK)
jnz 7b
mov %rsi, 0x130(%rsp) // signal mask at time of segmentation fault
11:xor %rax, %rax
test %rdx, %rdx // only return old mask, if set is non-NULL
jz 7b
mov %r10, 0(%rdx) // old_set
jmp 7b
// Handle rt_sigreturn()
12:cmp $15, %rax // NR_rt_sigreturn
jnz 14f
mov 0xA8(%rsp), %rsp // %rsp at time of segmentation fault
13:syscall // rt_sigreturn() is unrestricted
mov $66, %edi // rt_sigreturn() should never return
mov $231, %eax // NR_exit_group
jmp 13b
// Copy signal frame onto new stack. See clone.cc for details
14:cmp $56+0xF000, %rax // NR_clone + 0xF000
jnz 15f
lea 8(%rsp), %rax // retain stack frame upon returning
mov %rax, 0xA8(%rsp) // %rsp at time of segmentation fault
jmp 7b
// Forward system call to syscallEntryPointWithFrame()
15:lea 7b(%rip), %rcx
push %rcx
push 0xB8(%rsp) // %rip at time of segmentation fault
lea playground$syscallEntryPointWithFrame(%rip), %rcx
jmp *%rcx
// 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
cmp %r14, %r15
jnz 17f // check if returning from user's handler
decl %gs:0x105C-0xE0 // decrement SEGV recursion counter
mov 0xA8(%rsp), %rsp // %rsp at time of segmentation fault
mov $0xF, %eax // NR_rt_sigreturn
syscall
// 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
cmpq $0, 0(%rax) // SIG_DFL
jz 18f
cmpq $1, 0(%rax) // SIG_IGN
jnz 19f // 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) // signal mask at time of segmentation fault
ret
// 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 // check if we failed inside of SEGV handler
jnz 18b // if so, then terminate program
mov 0(%rax), %rbx // sa_segv_.sa_sigaction
mov 8(%rax), %rcx // sa_segv_.sa_flags
btl $31, %ecx // SA_RESETHAND
jnc 20f
movq $0, 0(%rax) // set handler to SIG_DFL
20:btl $30, %ecx // SA_NODEFER
jc 21f
mov %r14, 0(%rsp) // trigger a SEGV on return, so that we can
incl %gs:0x105C-0xE0 // clean up state; incr. recursion counter
21:jmp *%rbx // 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 // move code into non-executable section
22:mov $0xF, %rax // gdb looks for this signature when doing
syscall // backtraces
.popsection
.L_handle_callback_request:
mov 0x90(%rsp), %rax // %rdx at time of segmentation fault
jmp *%rax