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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
|
; This Source Code Form is subject to the terms of the Mozilla Public
; License, v. 2.0. If a copy of the MPL was not distributed with this
; file, You can obtain one at http://mozilla.org/MPL/2.0/.
; Comments indicate stack memory layout during execution.
; For example at the top of a function, where RIP just points to the return
; address, the stack looks like
; rip = [ra]
; And after pushing rax to the stack,
; rip = [rax][ra]
; And then, after allocating 20h bytes on the stack,
; rip = [..20..][rax][ra]
; And then, after pushing a function pointer,
; rip = [pfn][..20..][rax][ra]
include ksamd64.inc
.code
; It helps to add padding between functions so they're not right up against
; each other. Adds clarity to debugging, and gives a bit of leeway when
; searching for symbols (e.g. a function whose last instruction is CALL
; would push a return address that's in the next function.)
PaddingBetweenFunctions macro
repeat 10h
int 3
endm
endm
DoCrash macro
mov rax, 7
mov byte ptr [rax], 9
endm
PaddingBetweenFunctions
; There is no rip addressing mode in x64. The only way to get the value
; of rip is to call a function, and pop it from the stack.
WhoCalledMe proc
pop rax ; rax is now ra
push rax ; Restore ra so this function can return.
sub rax, 5 ; Correct for the size of the call instruction
ret
WhoCalledMe endp
PaddingBetweenFunctions
; Any function that we expect to test against on the stack, we'll need its
; real address. If we use function pointers in C, we'll get the address to jump
; table entries. This bit of code at the beginning of each function will
; return the real address we'd expect to see in stack traces.
;
; rcx (1st arg) = mode
; rax (return) = address of either NO_MANS_LAND or this function.
;
; When mode is 0, we place the address of NO_MANS_LAND in RAX, for the function
; to use as it wants. This is just for convenience because almost all functions
; here need this address at some point.
;
; When mode is 1, the address of this function is returned.
TestHeader macro
call WhoCalledMe
test rcx, rcx
je continue_test
ret
continue_test:
inc rcx
call x64CrashCFITest_NO_MANS_LAND
xor rcx, rcx
endm
; The point of this is to add a stack frame to test against.
; void* x64CrashCFITest_Launcher(int getAddress, void* pTestFn)
x64CrashCFITest_Launcher proc frame
TestHeader
.endprolog
call rdx
ret
x64CrashCFITest_Launcher endp
PaddingBetweenFunctions
; void* x64CrashCFITest_NO_MANS_LAND(uint64_t mode);
; Not meant to be called. Only when mode = 1 in order to return its address.
; Place this function's address on the stack so the stack scanning algorithm
; thinks this is a return address, and places it on the stack trace.
x64CrashCFITest_NO_MANS_LAND proc frame
TestHeader
.endprolog
ret
x64CrashCFITest_NO_MANS_LAND endp
PaddingBetweenFunctions
; Test that we:
; - handle unknown opcodes gracefully
; - fall back to other stack unwind strategies if CFI doesn't work
;
; In order to properly unwind this frame, we'd need to fully support
; SET_FPREG with offsets, plus restoring registers via PUSH_NONVOL.
; To do this, sprinkle the stack with bad return addresses
; and stack pointers.
x64CrashCFITest_UnknownOpcode proc frame
TestHeader
push rax
.allocstack 8
push rbp
.pushreg rbp
push rax
push rsp
push rax
push rsp
.allocstack 20h
; rsp = [rsp][pfn][rsp][pfn][rbp][pfn][ra]
lea rbp, [rsp+10h]
.setframe rbp, 10h
; rsp = [rsp][pfn] [rsp][pfn][rbp][pfn][ra]
; rbp = ^
.endprolog
; Now modify RSP so measuring stack size from unwind ops will not help
; finding the return address.
push rax
push rsp
; rsp = [rsp][pfn][rsp][pfn] [rsp][pfn][rbp][pfn][ra]
DoCrash
x64CrashCFITest_UnknownOpcode endp
PaddingBetweenFunctions
; void* x64CrashCFITest_PUSH_NONVOL(uint64_t mode);
;
; Test correct handling of PUSH_NONVOL unwind code.
;
x64CrashCFITest_PUSH_NONVOL proc frame
TestHeader
push r10
.pushreg r10
push r15
.pushreg r15
push rbx
.pushreg rbx
push rsi
.pushreg rsi
push rbp
.pushreg rbp
; rsp = [rbp][rsi][rbx][r15][r10][ra]
push rax
.allocstack 8
; rsp = [pfn][rbp][rsi][rbx][r15][r10][ra]
.endprolog
DoCrash
x64CrashCFITest_PUSH_NONVOL endp
PaddingBetweenFunctions
; void* x64CrashCFITest_ALLOC_SMALL(uint64_t mode);
;
; Small allocations are between 8bytes and 512kb-8bytes
;
x64CrashCFITest_ALLOC_SMALL proc frame
TestHeader
push rax
push rax
push rax
push rax
.allocstack 20h
; rsp = [pfn][pfn][pfn][pfn][ra]
.endprolog
DoCrash
x64CrashCFITest_ALLOC_SMALL endp
PaddingBetweenFunctions
; void* x64CrashCFITest_ALLOC_LARGE(uint64_t mode);
;
; Allocations between 512kb and 4gb
; Note: ReserveStackSpace() in nsTestCrasher.cpp pre-allocates stack
; space for this.
x64CrashCFITest_ALLOC_LARGE proc frame
TestHeader
sub rsp, 0a000h
.allocstack 0a000h
; rsp = [..640kb..][ra]
mov qword ptr [rsp], rax
; rsp = [pfn][..640kb-8..][ra]
.endprolog
DoCrash
x64CrashCFITest_ALLOC_LARGE endp
PaddingBetweenFunctions
; void* x64CrashCFITest_SAVE_NONVOL(uint64_t mode);
;
; Test correct handling of SAVE_NONVOL unwind code.
;
x64CrashCFITest_SAVE_NONVOL proc frame
TestHeader
sub rsp, 30h
.allocstack 30h
; rsp = [..30..][ra]
mov qword ptr [rsp+28h], r10
.savereg r10, 28h
mov qword ptr [rsp+20h], rbp
.savereg rbp, 20h
mov qword ptr [rsp+18h], rsi
.savereg rsi, 18h
mov qword ptr [rsp+10h], rbx
.savereg rbx, 10h
mov qword ptr [rsp+8], r15
.savereg r15, 8
; rsp = [r15][rbx][rsi][rbp][r10][ra]
mov qword ptr [rsp], rax
; rsp = [pfn][r15][rbx][rsi][rbp][r10][ra]
.endprolog
DoCrash
x64CrashCFITest_SAVE_NONVOL endp
PaddingBetweenFunctions
; void* x64CrashCFITest_SAVE_NONVOL_FAR(uint64_t mode);
;
; Similar to the test above but adding 640kb to most offsets.
; Note: ReserveStackSpace() in nsTestCrasher.cpp pre-allocates stack
; space for this.
x64CrashCFITest_SAVE_NONVOL_FAR proc frame
TestHeader
sub rsp, 0a0030h
.allocstack 0a0030h
; rsp = [..640k..][..30..][ra]
mov qword ptr [rsp+28h+0a0000h], r10
.savereg r10, 28h+0a0000h
mov qword ptr [rsp+20h+0a0000h], rbp
.savereg rbp, 20h+0a0000h
mov qword ptr [rsp+18h+0a0000h], rsi
.savereg rsi, 18h+0a0000h
mov qword ptr [rsp+10h+0a0000h], rbx
.savereg rbx, 10h+0a0000h
mov qword ptr [rsp+8+0a0000h], r15
.savereg r15, 8+0a0000h
; rsp = [..640k..][..8..][r15][rbx][rsi][rbp][r10][ra]
mov qword ptr [rsp], rax
; rsp = [pfn][..640k..][r15][rbx][rsi][rbp][r10][ra]
.endprolog
DoCrash
x64CrashCFITest_SAVE_NONVOL_FAR endp
PaddingBetweenFunctions
; void* x64CrashCFITest_SAVE_XMM128(uint64_t mode);
;
; Test correct handling of SAVE_XMM128 unwind code.
x64CrashCFITest_SAVE_XMM128 proc frame
TestHeader
sub rsp, 30h
.allocstack 30h
; rsp = [..30..][ra]
movdqu [rsp+20h], xmm6
.savexmm128 xmm6, 20h
; rsp = [..20..][xmm6][ra]
movdqu [rsp+10h], xmm15
.savexmm128 xmm15, 10h
; rsp = [..10..][xmm15][xmm6][ra]
mov qword ptr [rsp], rax
; rsp = [pfn][..8..][xmm15][xmm6][ra]
.endprolog
DoCrash
x64CrashCFITest_SAVE_XMM128 endp
PaddingBetweenFunctions
; void* x64CrashCFITest_SAVE_XMM128(uint64_t mode);
;
; Similar to the test above but adding 640kb to most offsets.
; Note: ReserveStackSpace() in nsTestCrasher.cpp pre-allocates stack
; space for this.
x64CrashCFITest_SAVE_XMM128_FAR proc frame
TestHeader
sub rsp, 0a0030h
.allocstack 0a0030h
; rsp = [..640kb..][..30..][ra]
movdqu [rsp+20h+0a0000h], xmm6
.savexmm128 xmm6, 20h+0a0000h
; rsp = [..640kb..][..20..][xmm6][ra]
movdqu [rsp+10h+0a0000h], xmm6
.savexmm128 xmm15, 10h+0a0000h
; rsp = [..640kb..][..10..][xmm15][xmm6][ra]
mov qword ptr [rsp], rax
; rsp = [pfn][..640kb..][..8..][xmm15][xmm6][ra]
.endprolog
DoCrash
x64CrashCFITest_SAVE_XMM128_FAR endp
PaddingBetweenFunctions
; void* x64CrashCFITest_EPILOG(uint64_t mode);
;
; The epilog unwind op will also set the unwind version to 2.
; Test that we don't choke on UWOP_EPILOG or version 2 unwind info.
x64CrashCFITest_EPILOG proc frame
TestHeader
push rax
.allocstack 8
; rsp = [pfn][ra]
.endprolog
DoCrash
.beginepilog
ret
x64CrashCFITest_EPILOG endp
PaddingBetweenFunctions
; Having an EOF symbol at the end of this file contains symbolication to this
; file. So addresses beyond this file don't get mistakenly symbolicated as a
; meaningful function name.
x64CrashCFITest_EOF proc frame
TestHeader
.endprolog
ret
x64CrashCFITest_EOF endp
end
|