mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-29 08:39:44 +08:00
e04194afd9
TerminateProcess(), like most of the Windows API, is declared WINAPI, which is __stdcall on 32-bit x86. That means that the callee, TerminateProcess() itself, is responsible for cleaning up parameters on the stack on return. In https://crashpad.chromium.org/bug/179, crashes in ExceptionHandlerServer::OnNonCrashDumpEvent() were observed in ways that make it evident that TerminateProcess() has been patched with a __cdecl routine. The crucial difference between __stdcall and __cdecl is that the caller is responsible for stack parameter cleanup in __cdecl. The mismatch means that nobody cleans parameters from the stack, and the stack pointer has an unexpected value, which in the case of the Crashpad handler crash, results in TerminateProcess()’s second argument erroneously being used as the lock address in the call to ReleaseSRWLockExclusive() or LeaveCriticalSection(). As a workaround, on 32-bit x86, call through SafeTerminateProcess(), a custom assembly routine that’s compatible with either __stdcall or __cdecl implementations of TerminateProcess() by not trusting the value of the stack pointer on return from that function. Instead, the stack pointer is restored directly from the frame pointer. Bug: crashpad:179 Test: crashpad_util_test SafeTerminateProcess.*, others Change-Id: If9508f4eb7631020ea69ddbbe4a22eb335cdb325 Reviewed-on: https://chromium-review.googlesource.com/481180 Reviewed-by: Scott Graham <scottmg@chromium.org>
531 lines
14 KiB
NASM
531 lines
14 KiB
NASM
; Copyright 2015 The Crashpad Authors. All rights reserved.
|
||
;
|
||
; Licensed under the Apache License, Version 2.0 (the "License");
|
||
; you may not use this file except in compliance with the License.
|
||
; You may obtain a copy of the License at
|
||
;
|
||
; http://www.apache.org/licenses/LICENSE-2.0
|
||
;
|
||
; Unless required by applicable law or agreed to in writing, software
|
||
; distributed under the License is distributed on an "AS IS" BASIS,
|
||
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
; See the License for the specific language governing permissions and
|
||
; limitations under the License.
|
||
|
||
; Detect ml64 assembling for x86_64 by checking for rax.
|
||
ifdef rax
|
||
_M_X64 equ 1
|
||
else
|
||
_M_IX86 equ 1
|
||
endif
|
||
|
||
ifdef _M_IX86
|
||
.586
|
||
.xmm
|
||
.model flat
|
||
endif
|
||
|
||
offsetof macro structure, field
|
||
exitm <structure.&field>
|
||
endm
|
||
|
||
; The CONTEXT structure definitions that follow are based on those in <winnt.h>.
|
||
; Field names are prefixed (as in c_Rax) to avoid colliding with the predefined
|
||
; register names (such as Rax).
|
||
|
||
ifdef _M_IX86
|
||
|
||
CONTEXT_i386 equ 10000h
|
||
CONTEXT_CONTROL equ CONTEXT_i386 or 1h
|
||
CONTEXT_INTEGER equ CONTEXT_i386 or 2h
|
||
CONTEXT_SEGMENTS equ CONTEXT_i386 or 4h
|
||
CONTEXT_FLOATING_POINT equ CONTEXT_i386 or 8h
|
||
CONTEXT_DEBUG_REGISTERS equ CONTEXT_i386 or 10h
|
||
CONTEXT_EXTENDED_REGISTERS equ CONTEXT_i386 or 20h
|
||
CONTEXT_XSTATE equ CONTEXT_i386 or 40h
|
||
|
||
MAXIMUM_SUPPORTED_EXTENSION equ 512
|
||
|
||
CONTEXT struct
|
||
c_ContextFlags dword ?
|
||
|
||
c_Dr0 dword ?
|
||
c_Dr1 dword ?
|
||
c_Dr2 dword ?
|
||
c_Dr3 dword ?
|
||
c_Dr6 dword ?
|
||
c_Dr7 dword ?
|
||
|
||
struct c_FloatSave
|
||
f_ControlWord dword ?
|
||
f_StatusWord dword ?
|
||
f_TagWord dword ?
|
||
f_ErrorOffset dword ?
|
||
f_ErrorSelector dword ?
|
||
f_DataOffset dword ?
|
||
f_DataSelector dword ?
|
||
f_RegisterArea byte 80 dup(?)
|
||
|
||
union
|
||
f_Spare0 dword ? ; As in FLOATING_SAVE_AREA.
|
||
f_Cr0NpxState dword ? ; As in WOW64_FLOATING_SAVE_AREA.
|
||
ends
|
||
ends
|
||
|
||
c_SegGs dword ?
|
||
c_SegFs dword ?
|
||
c_SegEs dword ?
|
||
c_SegDs dword ?
|
||
|
||
c_Edi dword ?
|
||
c_Esi dword ?
|
||
c_Ebx dword ?
|
||
c_Edx dword ?
|
||
c_Ecx dword ?
|
||
c_Eax dword ?
|
||
|
||
c_Ebp dword ?
|
||
|
||
c_Eip dword ?
|
||
c_SegCs dword ?
|
||
|
||
c_EFlags dword ?
|
||
|
||
c_Esp dword ?
|
||
c_SegSs dword ?
|
||
|
||
c_ExtendedRegisters byte MAXIMUM_SUPPORTED_EXTENSION dup(?)
|
||
CONTEXT ends
|
||
|
||
elseifdef _M_X64
|
||
|
||
M128A struct 16
|
||
m_Low qword ?
|
||
m_High qword ?
|
||
M128A ends
|
||
|
||
CONTEXT_AMD64 equ 100000h
|
||
CONTEXT_CONTROL equ CONTEXT_AMD64 or 1h
|
||
CONTEXT_INTEGER equ CONTEXT_AMD64 or 2h
|
||
CONTEXT_SEGMENTS equ CONTEXT_AMD64 or 4h
|
||
CONTEXT_FLOATING_POINT equ CONTEXT_AMD64 or 8h
|
||
CONTEXT_DEBUG_REGISTERS equ CONTEXT_AMD64 or 10h
|
||
CONTEXT_XSTATE equ CONTEXT_AMD64 or 40h
|
||
|
||
CONTEXT struct 16
|
||
c_P1Home qword ?
|
||
c_P2Home qword ?
|
||
c_P3Home qword ?
|
||
c_P4Home qword ?
|
||
c_P5Home qword ?
|
||
c_P6Home qword ?
|
||
|
||
c_ContextFlags dword ?
|
||
c_MxCsr dword ?
|
||
|
||
c_SegCs word ?
|
||
c_SegDs word ?
|
||
c_SegEs word ?
|
||
c_SegFs word ?
|
||
c_SegGs word ?
|
||
c_SegSs word ?
|
||
|
||
c_EFlags dword ?
|
||
|
||
c_Dr0 qword ?
|
||
c_Dr1 qword ?
|
||
c_Dr2 qword ?
|
||
c_Dr3 qword ?
|
||
c_Dr6 qword ?
|
||
c_Dr7 qword ?
|
||
|
||
c_Rax qword ?
|
||
c_Rcx qword ?
|
||
c_Rdx qword ?
|
||
c_Rbx qword ?
|
||
c_Rsp qword ?
|
||
c_Rbp qword ?
|
||
c_Rsi qword ?
|
||
c_Rdi qword ?
|
||
c_R8 qword ?
|
||
c_R9 qword ?
|
||
c_R10 qword ?
|
||
c_R11 qword ?
|
||
c_R12 qword ?
|
||
c_R13 qword ?
|
||
c_R14 qword ?
|
||
c_R15 qword ?
|
||
|
||
c_Rip qword ?
|
||
|
||
union
|
||
struct c_FltSave
|
||
f_ControlWord word ?
|
||
f_StatusWord word ?
|
||
f_TagWord byte ?
|
||
f_Reserved1 byte ?
|
||
f_ErrorOpcode word ?
|
||
f_ErrorOffset dword ?
|
||
f_ErrorSelector word ?
|
||
f_Reserved2 word ?
|
||
f_DataOffset dword ?
|
||
f_DataSelector word ?
|
||
f_Reserved3 word ?
|
||
f_MxCsr dword ?
|
||
f_MxCsr_Mask dword ?
|
||
f_FloatRegisters M128A 8 dup(<?>)
|
||
f_XmmRegisters M128A 16 dup(<?>)
|
||
f_Reserved4 byte 96 dup(?)
|
||
ends
|
||
struct
|
||
fx_Header M128A 2 dup(<?>)
|
||
fx_Legacy M128A 8 dup(<?>)
|
||
fx_Xmm0 M128A <?>
|
||
fx_Xmm1 M128A <?>
|
||
fx_Xmm2 M128A <?>
|
||
fx_Xmm3 M128A <?>
|
||
fx_Xmm4 M128A <?>
|
||
fx_Xmm5 M128A <?>
|
||
fx_Xmm6 M128A <?>
|
||
fx_Xmm7 M128A <?>
|
||
fx_Xmm8 M128A <?>
|
||
fx_Xmm9 M128A <?>
|
||
fx_Xmm10 M128A <?>
|
||
fx_Xmm11 M128A <?>
|
||
fx_Xmm12 M128A <?>
|
||
fx_Xmm13 M128A <?>
|
||
fx_Xmm14 M128A <?>
|
||
fx_Xmm15 M128A <?>
|
||
ends
|
||
ends
|
||
|
||
c_VectorRegister M128A 26 dup(<?>)
|
||
c_VectorControl qword ?
|
||
|
||
c_DebugControl qword ?
|
||
c_LastBranchToRip qword ?
|
||
c_LastBranchFromRip qword ?
|
||
c_LastExceptionToRip qword ?
|
||
c_LastExceptionFromRip qword ?
|
||
CONTEXT ends
|
||
|
||
endif
|
||
|
||
; namespace crashpad {
|
||
; void CaptureContext(CONTEXT* context);
|
||
; } // namespace crashpad
|
||
ifdef _M_IX86
|
||
CAPTURECONTEXT_SYMBOL equ ?CaptureContext@crashpad@@YAXPAU_CONTEXT@@@Z
|
||
elseifdef _M_X64
|
||
CAPTURECONTEXT_SYMBOL equ ?CaptureContext@crashpad@@YAXPEAU_CONTEXT@@@Z
|
||
endif
|
||
|
||
_TEXT segment
|
||
public CAPTURECONTEXT_SYMBOL
|
||
|
||
ifdef _M_IX86
|
||
|
||
CAPTURECONTEXT_SYMBOL proc
|
||
|
||
push ebp
|
||
mov ebp, esp
|
||
|
||
; pushfd first, because some instructions affect eflags. eflags will be in
|
||
; [ebp-4].
|
||
pushfd
|
||
|
||
; Save the original value of ebx, and use ebx to hold the CONTEXT* argument.
|
||
; The original value of ebx will be in [ebp-8].
|
||
push ebx
|
||
mov ebx, [ebp+8]
|
||
|
||
; General-purpose registers whose values haven’t changed can be captured
|
||
; directly.
|
||
mov [ebx.CONTEXT].c_Edi, edi
|
||
mov [ebx.CONTEXT].c_Esi, esi
|
||
mov [ebx.CONTEXT].c_Edx, edx
|
||
mov [ebx.CONTEXT].c_Ecx, ecx
|
||
mov [ebx.CONTEXT].c_Eax, eax
|
||
|
||
; Now that the original value of edx has been saved, it can be repurposed to
|
||
; hold other registers’ values.
|
||
|
||
; The original ebx was saved on the stack above.
|
||
mov edx, dword ptr [ebp-8]
|
||
mov [ebx.CONTEXT].c_Ebx, edx
|
||
|
||
; The original ebp was saved on the stack in this function’s prologue.
|
||
mov edx, dword ptr [ebp]
|
||
mov [ebx.CONTEXT].c_Ebp, edx
|
||
|
||
; eip can’t be accessed directly, but the return address saved on the stack
|
||
; by the call instruction that reached this function can be used.
|
||
mov edx, dword ptr [ebp+4]
|
||
mov [ebx.CONTEXT].c_Eip, edx
|
||
|
||
; The original eflags was saved on the stack above.
|
||
mov edx, dword ptr [ebp-4]
|
||
mov [ebx.CONTEXT].c_EFlags, edx
|
||
|
||
; esp was saved in ebp in this function’s prologue, but the caller’s esp is 8
|
||
; more than this value: 4 for the original ebp saved on the stack in this
|
||
; function’s prologue, and 4 for the return address saved on the stack by the
|
||
; call instruction that reached this function.
|
||
lea edx, [ebp+8]
|
||
mov [ebx.CONTEXT].c_Esp, edx
|
||
|
||
; The segment registers are 16 bits wide, but CONTEXT declares them as
|
||
; unsigned 32-bit values, so zero the top half.
|
||
xor edx, edx
|
||
mov dx, gs
|
||
mov [ebx.CONTEXT].c_SegGs, edx
|
||
mov dx, fs
|
||
mov [ebx.CONTEXT].c_SegFs, edx
|
||
mov dx, es
|
||
mov [ebx.CONTEXT].c_SegEs, edx
|
||
mov dx, ds
|
||
mov [ebx.CONTEXT].c_SegDs, edx
|
||
mov dx, cs
|
||
mov [ebx.CONTEXT].c_SegCs, edx
|
||
mov dx, ss
|
||
mov [ebx.CONTEXT].c_SegSs, edx
|
||
|
||
; Prepare for the string move that will populate the ExtendedRegisters area,
|
||
; or the string store that will zero it.
|
||
cld
|
||
|
||
; Use cpuid 1 to check whether fxsave is supported. If it is, perform it
|
||
; before fnsave because fxsave is a less-destructive operation.
|
||
mov esi, ebx
|
||
mov eax, 1
|
||
cpuid
|
||
mov ebx, esi
|
||
|
||
test edx, 01000000 ; FXSR
|
||
jnz $FXSave
|
||
|
||
; fxsave is not supported. Set ContextFlags to not include
|
||
; CONTEXT_EXTENDED_REGISTERS, and zero the ExtendedRegisters area.
|
||
mov [ebx.CONTEXT].c_ContextFlags, CONTEXT_i386 or \
|
||
CONTEXT_CONTROL or \
|
||
CONTEXT_INTEGER or \
|
||
CONTEXT_SEGMENTS or \
|
||
CONTEXT_FLOATING_POINT
|
||
lea edi, [ebx.CONTEXT].c_ExtendedRegisters
|
||
xor eax, eax
|
||
mov ecx, MAXIMUM_SUPPORTED_EXTENSION / sizeof(dword) ; 128
|
||
rep stosd
|
||
jmp $FXSaveDone
|
||
|
||
$FXSave:
|
||
; fxsave is supported. Set ContextFlags to include CONTEXT_EXTENDED_REGISTERS.
|
||
mov [ebx.CONTEXT].c_ContextFlags, CONTEXT_i386 or \
|
||
CONTEXT_CONTROL or \
|
||
CONTEXT_INTEGER or \
|
||
CONTEXT_SEGMENTS or \
|
||
CONTEXT_FLOATING_POINT or \
|
||
CONTEXT_EXTENDED_REGISTERS
|
||
|
||
; fxsave requires a 16 byte-aligned destination memory area. Nothing
|
||
; guarantees the alignment of a CONTEXT structure, so create a temporary
|
||
; aligned fxsave destination on the stack.
|
||
and esp, 0fffffff0h
|
||
sub esp, MAXIMUM_SUPPORTED_EXTENSION
|
||
|
||
; Zero out the temporary fxsave area before performing the fxsave. Some of the
|
||
; fxsave area may not be written by fxsave, and some is definitely not written
|
||
; by fxsave.
|
||
mov edi, esp
|
||
xor eax, eax
|
||
mov ecx, MAXIMUM_SUPPORTED_EXTENSION / sizeof(dword) ; 128
|
||
rep stosd
|
||
|
||
fxsave [esp]
|
||
|
||
; Copy the temporary fxsave area into the CONTEXT structure.
|
||
lea edi, [ebx.CONTEXT].c_ExtendedRegisters
|
||
mov esi, esp
|
||
mov ecx, MAXIMUM_SUPPORTED_EXTENSION / sizeof(dword) ; 128
|
||
rep movsd
|
||
|
||
; Free the stack space used for the temporary fxsave area.
|
||
lea esp, [ebp-8]
|
||
|
||
; TODO(mark): AVX/xsave support. https://crashpad.chromium.org/bug/58
|
||
|
||
$FXSaveDone:
|
||
; fnsave reinitializes the FPU with an implicit finit operation, so use frstor
|
||
; to restore the original state.
|
||
fnsave [ebx.CONTEXT].c_FloatSave
|
||
frstor [ebx.CONTEXT].c_FloatSave
|
||
|
||
; cr0 is inaccessible from user code, and this field would not be used anyway.
|
||
mov [ebx.CONTEXT].c_FloatSave.f_Cr0NpxState, 0
|
||
|
||
; The debug registers can’t be read from user code, so zero them out in the
|
||
; CONTEXT structure. context->ContextFlags doesn’t indicate that they are
|
||
; present.
|
||
mov [ebx.CONTEXT].c_Dr0, 0
|
||
mov [ebx.CONTEXT].c_Dr1, 0
|
||
mov [ebx.CONTEXT].c_Dr2, 0
|
||
mov [ebx.CONTEXT].c_Dr3, 0
|
||
mov [ebx.CONTEXT].c_Dr6, 0
|
||
mov [ebx.CONTEXT].c_Dr7, 0
|
||
|
||
; Clean up by restoring clobbered registers, even those considered volatile
|
||
; by the ABI, so that the captured context represents the state at this
|
||
; function’s exit.
|
||
mov edi, [ebx.CONTEXT].c_Edi
|
||
mov esi, [ebx.CONTEXT].c_Esi
|
||
mov edx, [ebx.CONTEXT].c_Edx
|
||
mov ecx, [ebx.CONTEXT].c_Ecx
|
||
mov eax, [ebx.CONTEXT].c_Eax
|
||
pop ebx
|
||
popfd
|
||
|
||
pop ebp
|
||
|
||
ret
|
||
|
||
CAPTURECONTEXT_SYMBOL endp
|
||
|
||
elseifdef _M_X64
|
||
|
||
CAPTURECONTEXT_SYMBOL proc frame
|
||
|
||
push rbp
|
||
.pushreg rbp
|
||
mov rbp, rsp
|
||
.setframe rbp, 0
|
||
|
||
; Note that 16-byte stack alignment is not maintained because this function
|
||
; does not call out to any other.
|
||
|
||
; pushfq first, because some instructions affect rflags. rflags will be in
|
||
; [rbp-8].
|
||
pushfq
|
||
.allocstack 8
|
||
.endprolog
|
||
|
||
mov [rcx.CONTEXT].c_ContextFlags, CONTEXT_AMD64 or \
|
||
CONTEXT_CONTROL or \
|
||
CONTEXT_INTEGER or \
|
||
CONTEXT_SEGMENTS or \
|
||
CONTEXT_FLOATING_POINT
|
||
|
||
; General-purpose registers whose values haven’t changed can be captured
|
||
; directly.
|
||
mov [rcx.CONTEXT].c_Rax, rax
|
||
mov [rcx.CONTEXT].c_Rdx, rdx
|
||
mov [rcx.CONTEXT].c_Rbx, rbx
|
||
mov [rcx.CONTEXT].c_Rsi, rsi
|
||
mov [rcx.CONTEXT].c_Rdi, rdi
|
||
mov [rcx.CONTEXT].c_R8, r8
|
||
mov [rcx.CONTEXT].c_R9, r9
|
||
mov [rcx.CONTEXT].c_R10, r10
|
||
mov [rcx.CONTEXT].c_R11, r11
|
||
mov [rcx.CONTEXT].c_R12, r12
|
||
mov [rcx.CONTEXT].c_R13, r13
|
||
mov [rcx.CONTEXT].c_R14, r14
|
||
mov [rcx.CONTEXT].c_R15, r15
|
||
|
||
; Because of the calling convention, there’s no way to recover the value of
|
||
; the caller’s rcx as it existed prior to calling this function. This
|
||
; function captures a snapshot of the register state at its return, which
|
||
; involves rcx containing a pointer to its first argument.
|
||
mov [rcx.CONTEXT].c_Rcx, rcx
|
||
|
||
; Now that the original value of rax has been saved, it can be repurposed to
|
||
; hold other registers’ values.
|
||
|
||
; Save mxcsr. This is duplicated in context->FltSave.MxCsr, saved by fxsave
|
||
; below.
|
||
stmxcsr [rcx.CONTEXT].c_MxCsr
|
||
|
||
; Segment registers.
|
||
mov [rcx.CONTEXT].c_SegCs, cs
|
||
mov [rcx.CONTEXT].c_SegDs, ds
|
||
mov [rcx.CONTEXT].c_SegEs, es
|
||
mov [rcx.CONTEXT].c_SegFs, fs
|
||
mov [rcx.CONTEXT].c_SegGs, gs
|
||
mov [rcx.CONTEXT].c_SegSs, ss
|
||
|
||
; The original rflags was saved on the stack above. Note that the CONTEXT
|
||
; structure only stores eflags, the low 32 bits. The high 32 bits in rflags
|
||
; are reserved.
|
||
mov rax, qword ptr [rbp-8]
|
||
mov [rcx.CONTEXT].c_EFlags, eax
|
||
|
||
; rsp was saved in rbp in this function’s prologue, but the caller’s rsp is
|
||
; 16 more than this value: 8 for the original rbp saved on the stack in this
|
||
; function’s prologue, and 8 for the return address saved on the stack by the
|
||
; call instruction that reached this function.
|
||
lea rax, [rbp+16]
|
||
mov [rcx.CONTEXT].c_Rsp, rax
|
||
|
||
; The original rbp was saved on the stack in this function’s prologue.
|
||
mov rax, qword ptr [rbp]
|
||
mov [rcx.CONTEXT].c_Rbp, rax
|
||
|
||
; rip can’t be accessed directly, but the return address saved on the stack by
|
||
; the call instruction that reached this function can be used.
|
||
mov rax, qword ptr [rbp+8]
|
||
mov [rcx.CONTEXT].c_Rip, rax
|
||
|
||
; Zero out the fxsave area before performing the fxsave. Some of the fxsave
|
||
; area may not be written by fxsave, and some is definitely not written by
|
||
; fxsave. This also zeroes out the rest of the CONTEXT structure to its end,
|
||
; including the unused VectorRegister and VectorControl fields, and the debug
|
||
; control register fields.
|
||
mov rbx, rcx
|
||
cld
|
||
lea rdi, [rcx.CONTEXT].c_FltSave
|
||
xor rax, rax
|
||
mov rcx, (sizeof(CONTEXT) - offsetof(CONTEXT, c_FltSave)) / \
|
||
sizeof(qword) ; 122
|
||
rep stosq
|
||
mov rcx, rbx
|
||
|
||
; Save the floating point (including SSE) state. The CONTEXT structure is
|
||
; declared as 16-byte-aligned, which is correct for this operation.
|
||
fxsave [rcx.CONTEXT].c_FltSave
|
||
|
||
; TODO(mark): AVX/xsave support. https://crashpad.chromium.org/bug/58
|
||
|
||
; The register parameter home address fields aren’t used, so zero them out.
|
||
mov [rcx.CONTEXT].c_P1Home, 0
|
||
mov [rcx.CONTEXT].c_P2Home, 0
|
||
mov [rcx.CONTEXT].c_P3Home, 0
|
||
mov [rcx.CONTEXT].c_P4Home, 0
|
||
mov [rcx.CONTEXT].c_P5Home, 0
|
||
mov [rcx.CONTEXT].c_P6Home, 0
|
||
|
||
; The debug registers can’t be read from user code, so zero them out in the
|
||
; CONTEXT structure. context->ContextFlags doesn’t indicate that they are
|
||
; present.
|
||
mov [rcx.CONTEXT].c_Dr0, 0
|
||
mov [rcx.CONTEXT].c_Dr1, 0
|
||
mov [rcx.CONTEXT].c_Dr2, 0
|
||
mov [rcx.CONTEXT].c_Dr3, 0
|
||
mov [rcx.CONTEXT].c_Dr6, 0
|
||
mov [rcx.CONTEXT].c_Dr7, 0
|
||
|
||
; Clean up by restoring clobbered registers, even those considered volatile by
|
||
; the ABI, so that the captured context represents the state at this
|
||
; function’s exit.
|
||
mov rax, [rcx.CONTEXT].c_Rax
|
||
mov rbx, [rcx.CONTEXT].c_Rbx
|
||
mov rdi, [rcx.CONTEXT].c_Rdi
|
||
popfq
|
||
|
||
pop rbp
|
||
|
||
ret
|
||
|
||
CAPTURECONTEXT_SYMBOL endp
|
||
|
||
endif
|
||
|
||
_TEXT ends
|
||
end
|