1 | /* $Id: except-x86-vcc.cpp 97863 2022-12-23 17:01:49Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * IPRT - Visual C++ Compiler - x86 Exception Handler Filter.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2006-2022 Oracle and/or its affiliates.
|
---|
8 | *
|
---|
9 | * This file is part of VirtualBox base platform packages, as
|
---|
10 | * available from https://www.virtualbox.org.
|
---|
11 | *
|
---|
12 | * This program is free software; you can redistribute it and/or
|
---|
13 | * modify it under the terms of the GNU General Public License
|
---|
14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
15 | * License.
|
---|
16 | *
|
---|
17 | * This program is distributed in the hope that it will be useful, but
|
---|
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
20 | * General Public License for more details.
|
---|
21 | *
|
---|
22 | * You should have received a copy of the GNU General Public License
|
---|
23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
24 | *
|
---|
25 | * The contents of this file may alternatively be used under the terms
|
---|
26 | * of the Common Development and Distribution License Version 1.0
|
---|
27 | * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
|
---|
28 | * in the VirtualBox distribution, in which case the provisions of the
|
---|
29 | * CDDL are applicable instead of those of the GPL.
|
---|
30 | *
|
---|
31 | * You may elect to license modified versions of this file under the
|
---|
32 | * terms and conditions of either the GPL or the CDDL or both.
|
---|
33 | *
|
---|
34 | * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
|
---|
35 | */
|
---|
36 |
|
---|
37 |
|
---|
38 | /*********************************************************************************************************************************
|
---|
39 | * Header Files *
|
---|
40 | *********************************************************************************************************************************/
|
---|
41 | #include <iprt/win/windows.h>
|
---|
42 |
|
---|
43 | #include "except-vcc.h"
|
---|
44 |
|
---|
45 |
|
---|
46 | /*********************************************************************************************************************************
|
---|
47 | * Global Variables *
|
---|
48 | *********************************************************************************************************************************/
|
---|
49 | extern "C" uintptr_t __security_cookie;
|
---|
50 |
|
---|
51 |
|
---|
52 | /*********************************************************************************************************************************
|
---|
53 | * Internal Functions *
|
---|
54 | *********************************************************************************************************************************/
|
---|
55 | DECLASM(LONG) rtVccEh4DoFiltering(PFN_EH4_XCPT_FILTER_T pfnFilter, uint8_t const *pbFrame);
|
---|
56 | DECLASM(DECL_NO_RETURN(void)) rtVccEh4JumpToHandler(PFN_EH4_XCPT_HANDLER_T pfnHandler, uint8_t const *pbFrame);
|
---|
57 | DECLASM(void) rtVccEh4DoGlobalUnwind(PEXCEPTION_RECORD pXcptRec, PEXCEPTION_REGISTRATION_RECORD pXcptRegRec);
|
---|
58 | DECLASM(void) rtVccEh4DoFinally(PFN_EH4_FINALLY_T pfnFinally, bool fAbend, uint8_t const *pbFrame);
|
---|
59 |
|
---|
60 |
|
---|
61 | /**
|
---|
62 | * Calls the __finally blocks up to @a uTargetTryLevel is reached, starting with
|
---|
63 | * @a pEh4XcptRegRec->uTryLevel.
|
---|
64 | *
|
---|
65 | * @param pEh4XcptRegRec The EH4 exception registration record.
|
---|
66 | * @param uTargetTryLevel The target __try level to stop unwinding at.
|
---|
67 | * @param pbFrame The frame pointer (EBP).
|
---|
68 | */
|
---|
69 | static void rtVccEh4DoLocalUnwind(PEH4_XCPT_REG_REC_T pEh4XcptRegRec, uint32_t uTargetTryLevel, uint8_t const *pbFrame)
|
---|
70 | {
|
---|
71 | /*
|
---|
72 | * Manually set up exception handler.
|
---|
73 | */
|
---|
74 | /** @todo */
|
---|
75 |
|
---|
76 | /*
|
---|
77 | * Do the unwinding.
|
---|
78 | */
|
---|
79 | uint32_t uCurTryLevel = pEh4XcptRegRec->uTryLevel;
|
---|
80 | while ( uCurTryLevel != EH4_TOPMOST_TRY_LEVEL
|
---|
81 | && ( uCurTryLevel > uTargetTryLevel
|
---|
82 | || uTargetTryLevel == EH4_TOPMOST_TRY_LEVEL /* if we knew what 0xffffffff meant, this could probably be omitted */ ))
|
---|
83 | {
|
---|
84 | PCEH4_SCOPE_TAB_T const pScopeTable = (PCEH4_SCOPE_TAB_T)(pEh4XcptRegRec->uEncodedScopeTable ^ __security_cookie);
|
---|
85 | PCEH4_SCOPE_TAB_REC_T const pEntry = &pScopeTable->aScopeRecords[uCurTryLevel];
|
---|
86 |
|
---|
87 | pEh4XcptRegRec->uTryLevel = uCurTryLevel = pEntry->uEnclosingLevel;
|
---|
88 |
|
---|
89 | /* __finally scope table entries have no filter sub-function. */
|
---|
90 | if (!pEntry->pfnFilter)
|
---|
91 | {
|
---|
92 | //RTAssertMsg2("rtVccEh4DoLocalUnwind: Calling %p (level %#x)\n", pEntry->pfnFinally, uCurTryLevel);
|
---|
93 | rtVccEh4DoFinally(pEntry->pfnFinally, true /*fAbend*/, pbFrame);
|
---|
94 |
|
---|
95 | /* Read the try level again in case it changed... */
|
---|
96 | uCurTryLevel = pEh4XcptRegRec->uTryLevel;
|
---|
97 | }
|
---|
98 | }
|
---|
99 |
|
---|
100 | /*
|
---|
101 | * Deregister exception handler.
|
---|
102 | */
|
---|
103 | /** @todo */
|
---|
104 | }
|
---|
105 |
|
---|
106 |
|
---|
107 | DECLINLINE(void) rtVccValidateExceptionContextRecord(PCONTEXT pCpuCtx)
|
---|
108 | {
|
---|
109 | RT_NOREF(pCpuCtx);
|
---|
110 | /** @todo Implement __exception_validate_context_record .*/
|
---|
111 | }
|
---|
112 |
|
---|
113 |
|
---|
114 | /**
|
---|
115 | * Helper that validates stack cookies.
|
---|
116 | */
|
---|
117 | DECLINLINE(void) rtVccEh4ValidateCookies(PCEH4_SCOPE_TAB_T pScopeTable, uint8_t const *pbFrame)
|
---|
118 | {
|
---|
119 | if (pScopeTable->offGSCookie != EH4_NO_GS_COOKIE)
|
---|
120 | {
|
---|
121 | uintptr_t uGsCookie = *(uintptr_t const *)&pbFrame[pScopeTable->offGSCookie];
|
---|
122 | uGsCookie ^= (uintptr_t)&pbFrame[pScopeTable->offGSCookieXor];
|
---|
123 | __security_check_cookie(uGsCookie);
|
---|
124 | }
|
---|
125 |
|
---|
126 | uintptr_t uEhCookie = *(uintptr_t const *)&pbFrame[pScopeTable->offEHCookie];
|
---|
127 | uEhCookie ^= (uintptr_t)&pbFrame[pScopeTable->offEHCookieXor];
|
---|
128 | __security_check_cookie(uEhCookie);
|
---|
129 | }
|
---|
130 |
|
---|
131 |
|
---|
132 | /**
|
---|
133 | * Call exception filters, handlers and unwind code for x86 code.
|
---|
134 | *
|
---|
135 | * This is called for windows' structured exception handling (SEH) in x86 32-bit
|
---|
136 | * code, i.e. the __try/__except/__finally stuff in Visual C++. The compiler
|
---|
137 | * generate scope records for the __try/__except blocks as well as unwind
|
---|
138 | * records for __finally and probably C++ stack object destructors.
|
---|
139 | *
|
---|
140 | * @returns Exception disposition.
|
---|
141 | * @param pXcptRec The exception record.
|
---|
142 | * @param pXcptRegRec The exception registration record, taken to be the frame
|
---|
143 | * address.
|
---|
144 | * @param pCpuCtx The CPU context for the exception.
|
---|
145 | * @param pDispCtx Dispatcher context.
|
---|
146 | */
|
---|
147 | extern "C" __declspec(guard(suppress))
|
---|
148 | DWORD _except_handler4(PEXCEPTION_RECORD pXcptRec, PEXCEPTION_REGISTRATION_RECORD pXcptRegRec, PCONTEXT pCpuCtx, PVOID pvCtx)
|
---|
149 | {
|
---|
150 | /*
|
---|
151 | * The registration record (probably chained on FS:[0] like in the OS/2 days)
|
---|
152 | * points to a larger structure specific to _except_handler4. The structure
|
---|
153 | * is planted right after the saved caller EBP value when establishing the
|
---|
154 | * stack frame, so EBP = pXcptRegRec + 1;
|
---|
155 | */
|
---|
156 | PEH4_XCPT_REG_REC_T const pEh4XcptRegRec = RT_FROM_MEMBER(pXcptRegRec, EH4_XCPT_REG_REC_T, XcptRec);
|
---|
157 | uint8_t * const pbFrame = (uint8_t *)&pEh4XcptRegRec[1];
|
---|
158 | PCEH4_SCOPE_TAB_T const pScopeTable = (PCEH4_SCOPE_TAB_T)(pEh4XcptRegRec->uEncodedScopeTable ^ __security_cookie);
|
---|
159 |
|
---|
160 | /*
|
---|
161 | * Validate the stack cookie and exception context.
|
---|
162 | */
|
---|
163 | rtVccEh4ValidateCookies(pScopeTable, pbFrame);
|
---|
164 | rtVccValidateExceptionContextRecord(pCpuCtx);
|
---|
165 |
|
---|
166 | /*
|
---|
167 | * If dispatching an exception, call the exception filter functions and jump
|
---|
168 | * to the __except blocks if so directed.
|
---|
169 | */
|
---|
170 | if (IS_DISPATCHING(pXcptRec->ExceptionFlags))
|
---|
171 | {
|
---|
172 | uint32_t uTryLevel = pEh4XcptRegRec->uTryLevel;
|
---|
173 | //RTAssertMsg2("_except_handler4: dispatch: uTryLevel=%#x\n", uTryLevel);
|
---|
174 | while (uTryLevel != EH4_TOPMOST_TRY_LEVEL)
|
---|
175 | {
|
---|
176 | PCEH4_SCOPE_TAB_REC_T const pEntry = &pScopeTable->aScopeRecords[uTryLevel];
|
---|
177 | PFN_EH4_XCPT_FILTER_T const pfnFilter = pEntry->pfnFilter;
|
---|
178 | if (pfnFilter)
|
---|
179 | {
|
---|
180 | /* Call the __except filtering expression: */
|
---|
181 | //RTAssertMsg2("_except_handler4: Calling pfnFilter=%p\n", pfnFilter);
|
---|
182 | EXCEPTION_POINTERS XcptPtrs = { pXcptRec, pCpuCtx };
|
---|
183 | pEh4XcptRegRec->pXctpPtrs = &XcptPtrs;
|
---|
184 | LONG lRet = rtVccEh4DoFiltering(pfnFilter, pbFrame);
|
---|
185 | pEh4XcptRegRec->pXctpPtrs = NULL;
|
---|
186 | //RTAssertMsg2("_except_handler4: pfnFilter=%p -> %ld\n", pfnFilter, lRet);
|
---|
187 | rtVccEh4ValidateCookies(pScopeTable, pbFrame);
|
---|
188 |
|
---|
189 | /* Return if we're supposed to continue execution (the convention
|
---|
190 | it to match negative values rather than the exact defined value): */
|
---|
191 | AssertCompile(EXCEPTION_CONTINUE_EXECUTION == -1);
|
---|
192 | if (lRet <= EXCEPTION_CONTINUE_EXECUTION)
|
---|
193 | return ExceptionContinueExecution;
|
---|
194 |
|
---|
195 | /* Similarly, the handler is executed for any positive value. */
|
---|
196 | AssertCompile(EXCEPTION_CONTINUE_SEARCH == 0);
|
---|
197 | AssertCompile(EXCEPTION_EXECUTE_HANDLER == 1);
|
---|
198 | if (lRet >= EXCEPTION_EXECUTE_HANDLER)
|
---|
199 | {
|
---|
200 | /* We're about to resume execution in the __except block, so unwind
|
---|
201 | up to it first. */
|
---|
202 | //RTAssertMsg2("_except_handler4: global unwind\n");
|
---|
203 | rtVccEh4DoGlobalUnwind(pXcptRec, &pEh4XcptRegRec->XcptRec);
|
---|
204 | if (pEh4XcptRegRec->uTryLevel != EH4_TOPMOST_TRY_LEVEL)
|
---|
205 | {
|
---|
206 | //RTAssertMsg2("_except_handler4: local unwind\n");
|
---|
207 | rtVccEh4DoLocalUnwind(pEh4XcptRegRec, uTryLevel, pbFrame);
|
---|
208 | }
|
---|
209 | rtVccEh4ValidateCookies(pScopeTable, pbFrame);
|
---|
210 |
|
---|
211 | /* Now jump to the __except block. This will _not_ return. */
|
---|
212 | //RTAssertMsg2("_except_handler4: jumping to __except block %p (level %#x)\n", pEntry->pfnHandler, pEntry->uEnclosingLevel);
|
---|
213 | pEh4XcptRegRec->uTryLevel = pEntry->uEnclosingLevel;
|
---|
214 | rtVccEh4ValidateCookies(pScopeTable, pbFrame); /* paranoia^2 */
|
---|
215 |
|
---|
216 | rtVccEh4JumpToHandler(pEntry->pfnHandler, pbFrame);
|
---|
217 | /* (not reached) */
|
---|
218 | }
|
---|
219 | }
|
---|
220 |
|
---|
221 | /*
|
---|
222 | * Next try level.
|
---|
223 | */
|
---|
224 | uTryLevel = pEntry->uEnclosingLevel;
|
---|
225 | }
|
---|
226 | }
|
---|
227 | /*
|
---|
228 | * If not dispatching we're unwinding, so we call any __finally blocks.
|
---|
229 | */
|
---|
230 | else
|
---|
231 | {
|
---|
232 | //RTAssertMsg2("_except_handler4: unwind: uTryLevel=%#x\n", pEh4XcptRegRec->uTryLevel);
|
---|
233 | if (pEh4XcptRegRec->uTryLevel != EH4_TOPMOST_TRY_LEVEL)
|
---|
234 | {
|
---|
235 | rtVccEh4DoLocalUnwind(pEh4XcptRegRec, EH4_TOPMOST_TRY_LEVEL, pbFrame);
|
---|
236 | rtVccEh4ValidateCookies(pScopeTable, pbFrame);
|
---|
237 | }
|
---|
238 | }
|
---|
239 |
|
---|
240 | RT_NOREF(pvCtx);
|
---|
241 | return ExceptionContinueSearch;
|
---|
242 | }
|
---|
243 |
|
---|