-
Notifications
You must be signed in to change notification settings - Fork 0
/
fault_handler_x86_64.S
215 lines (200 loc) · 9.37 KB
/
fault_handler_x86_64.S
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
// 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