mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-24 10:45:42 +00:00
043bf247e7
MozReview-Commit-ID: DdnKFz73T3K --HG-- extra : amend_source : 0fc89a8da7ada1e97f88654372091234fe9534f4
379 lines
8.4 KiB
NASM
379 lines
8.4 KiB
NASM
; 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
|