diff options
Diffstat (limited to 'src/VBox/Runtime/common/compiler/vcc')
17 files changed, 3451 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/compiler/vcc/atexit-vcc.cpp b/src/VBox/Runtime/common/compiler/vcc/atexit-vcc.cpp new file mode 100644 index 00000000..f6fad758 --- /dev/null +++ b/src/VBox/Runtime/common/compiler/vcc/atexit-vcc.cpp @@ -0,0 +1,145 @@ +/* $Id: atexit-vcc.cpp $ */ +/** @file + * IPRT - Visual C++ Compiler - Simple atexit implementation. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "internal/iprt.h" + +#include <iprt/asm.h> +#include <iprt/mem.h> +#include <iprt/nocrt/stdlib.h> + +#include "internal/compiler-vcc.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef struct RTNOCRTATEXITCHUNK +{ + PFNRTNOCRTATEXITCALLBACK apfnCallbacks[256]; +} RTNOCRTATEXITCHUNK; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** The first atexit() registration chunk. */ +static RTNOCRTATEXITCHUNK g_aAtExitPrealloc; +/** Array of atexit() callback chunk pointers. */ +static RTNOCRTATEXITCHUNK *g_apAtExit[8192 / 256] = { &g_aAtExitPrealloc, }; +/** Chunk and callback index in one. */ +static volatile uint32_t g_idxNextAtExit = 0; + + +/* Note! not using atexit here because it'll clash with built-in prototype. */ +extern "C" int nocrt_atexit(PFNRTNOCRTATEXITCALLBACK pfnCallback) RT_NOEXCEPT +{ + AssertPtr(pfnCallback); + + /* + * Allocate a table index. + */ + uint32_t idx = ASMAtomicIncU32(&g_idxNextAtExit) - 1; + AssertReturnStmt(idx < RT_ELEMENTS(g_apAtExit) * RT_ELEMENTS(g_apAtExit[0]->apfnCallbacks), + ASMAtomicDecU32(&g_idxNextAtExit), -1); + + /* + * Make sure the table chunk is there. + */ + uint32_t idxChunk = idx / RT_ELEMENTS(g_apAtExit[0]->apfnCallbacks); + RTNOCRTATEXITCHUNK *pChunk = ASMAtomicReadPtrT(&g_apAtExit[idxChunk], RTNOCRTATEXITCHUNK *); + if (!pChunk) + { + pChunk = (RTNOCRTATEXITCHUNK *)RTMemAllocZ(sizeof(*pChunk)); /* ASSUMES that the allocator works w/o initialization! */ + AssertReturn(pChunk, -1); /* don't try decrement, someone could be racing us... */ + + if (!ASMAtomicCmpXchgPtr(&g_apAtExit[idxChunk], pChunk, NULL)) + { + RTMemFree(pChunk); + + pChunk = ASMAtomicReadPtrT(&g_apAtExit[idxChunk], RTNOCRTATEXITCHUNK *); + Assert(pChunk); + } + } + + /* + * Add our callback. + */ + pChunk->apfnCallbacks[idxChunk % RT_ELEMENTS(pChunk->apfnCallbacks)] = pfnCallback; + return 0; +} +RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(atexit); + + +void rtVccTermRunAtExit(void) RT_NOEXCEPT +{ + uint32_t idxAtExit = ASMAtomicReadU32(&g_idxNextAtExit); + if (idxAtExit-- > 0) + { + uint32_t idxChunk = idxAtExit / RT_ELEMENTS(g_apAtExit[0]->apfnCallbacks); + uint32_t idxCallback = idxAtExit % RT_ELEMENTS(g_apAtExit[0]->apfnCallbacks); + for (;;) + { + RTNOCRTATEXITCHUNK *pChunk = ASMAtomicReadPtrT(&g_apAtExit[idxChunk], RTNOCRTATEXITCHUNK *); + if (pChunk) + { + do + { + g_idxNextAtExit = idxAtExit--; /* Make sure we don't try problematic atexit callbacks! */ + + PFNRTNOCRTATEXITCALLBACK pfnCallback = pChunk->apfnCallbacks[idxCallback]; + if (pfnCallback) /* Can be NULL see registration code */ + { + pfnCallback(); + pChunk->apfnCallbacks[idxCallback] = NULL; + } + } while (idxCallback-- > 0); + } + else + idxAtExit -= RT_ELEMENTS(g_apAtExit[0]->apfnCallbacks); + if (idxChunk == 0) + break; + idxChunk--; + idxCallback = RT_ELEMENTS(g_apAtExit[0]->apfnCallbacks) - 1; + } + + g_idxNextAtExit = 0; + } +} + diff --git a/src/VBox/Runtime/common/compiler/vcc/except-seh-vcc.cpp b/src/VBox/Runtime/common/compiler/vcc/except-seh-vcc.cpp new file mode 100644 index 00000000..3a873673 --- /dev/null +++ b/src/VBox/Runtime/common/compiler/vcc/except-seh-vcc.cpp @@ -0,0 +1,243 @@ +/* $Id: except-seh-vcc.cpp $ */ +/** @file + * IPRT - Visual C++ Compiler - SEH exception handler (__try/__except/__finally). + */ + +/* + * Copyright (C) 2022-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "internal/nocrt.h" + +#include "except-vcc.h" + + +#if !defined(RT_ARCH_AMD64) +# error "This file is for AMD64 (and probably ARM, but needs porting)" +#endif + + +/** + * Calls an exception filter w/o doing any control guard checks. + * + * Doing this within an inline function to prevent disabling CFG for any other + * calls that __C_specific_handler might be doing. + * + * Presumably, the presumption here is that since the target address here is + * taken from tables assumed to be readonly and generated by the compiler, there + * is no need to do any CFG checks. Besides, the target isn't a function that + * is safe to be called out of context and thus doesn't belong in the CFG tables + * in any way. + */ +__declspec(guard(ignore)) +DECLINLINE(LONG) CallFilterFunction(PEXCEPTION_FILTER pfnFilter, PEXCEPTION_POINTERS pXcptPtrs, + PEXCEPTION_REGISTRATION_RECORD pXcptRegRec) +{ + return pfnFilter(pXcptPtrs, pXcptRegRec); +} + + +/** + * Calls an exception finally block w/o doing any control guard checks. + * + * See CallFilterFunction for details. + */ +__declspec(guard(ignore)) +DECLINLINE(void) CallFinallyFunction(PTERMINATION_HANDLER const pfnTermHandler, BOOLEAN fAbend, + PEXCEPTION_REGISTRATION_RECORD pXcptRegRec) +{ + pfnTermHandler(fAbend, pXcptRegRec); +} + + +/** + * Call exception filters, handlers and unwind code. + * + * This is called for windows' structured exception handling (SEH), i.e. the + * __try/__except/__finally stuff in Visual C++. The compiler generate scope + * records for the __try/__except blocks as well as unwind records for __finally + * and probably C++ stack object destructors. + * + * @returns Exception disposition. + * @param pXcptRec The exception record. + * @param pXcptRegRec The exception registration record, taken to be the frame + * address. + * @param pCpuCtx The CPU context for the exception. + * @param pDispCtx Dispatcher context. + */ +EXCEPTION_DISPOSITION __C_specific_handler(PEXCEPTION_RECORD pXcptRec, PEXCEPTION_REGISTRATION_RECORD pXcptRegRec, + PCONTEXT pCpuCtx, PDISPATCHER_CONTEXT pDispCtx) +{ + /* + * This function works the scope table, starting at ScopeIndex + * from the dispatcher context. + */ + SCOPE_TABLE const * const pScopeTab = (SCOPE_TABLE const *)pDispCtx->HandlerData; + uint32_t const cScopes = pScopeTab->Count; + uint32_t idxScope = pDispCtx->ScopeIndex; + + /* + * The table addresses are RVAs, so convert the program counter (PC) to an RVA. + */ + uint32_t const uRvaPc = pDispCtx->ControlPc - pDispCtx->ImageBase; + + /* + * Before we get any further, there are two types of scope records: + * 1. Unwind (aka termination) handler (JumpTarget == 0). + * 2. Exception filter & handler (JumpTarget != 0). + */ + + if (IS_DISPATCHING(pXcptRec->ExceptionFlags)) + { + /* + * Call exception filter functions when dispatching. + */ + for (; idxScope < cScopes; idxScope++) + { + /* Skip unwind entries (exception handler set to zero). */ + uint32_t const uXcptHandler = pScopeTab->ScopeRecord[idxScope].JumpTarget; + if (uXcptHandler != 0) + { + uint32_t const uBegin = pScopeTab->ScopeRecord[idxScope].BeginAddress; + uint32_t const uEnd = pScopeTab->ScopeRecord[idxScope].EndAddress; + uint32_t const cbScope = uEnd - uBegin; + if ( uRvaPc - uBegin < cbScope + && uBegin < uEnd /* paranoia */) + { + /* The special HandlerAddress value 1 translates to a + EXCEPTION_EXECUTE_HANDLER filter return value. */ + LONG lRet = EXCEPTION_EXECUTE_HANDLER; + uint32_t const uFltTermHandler = pScopeTab->ScopeRecord[idxScope].HandlerAddress; + if (uFltTermHandler != 1) + { + PEXCEPTION_FILTER const pfnFilter = (PEXCEPTION_FILTER)(pDispCtx->ImageBase + uFltTermHandler); + EXCEPTION_POINTERS XcptPtrs = { pXcptRec, pCpuCtx }; + lRet = CallFilterFunction(pfnFilter, &XcptPtrs, pXcptRegRec); + + AssertCompile(EXCEPTION_CONTINUE_SEARCH == 0); + if (lRet == EXCEPTION_CONTINUE_SEARCH) + continue; + } + + /* Return if we're supposed to continue execution (the convention + it to match negative values rather than the exact defined value): */ + AssertCompile(EXCEPTION_CONTINUE_EXECUTION == -1); + AssertCompile(EXCEPTION_EXECUTE_HANDLER == 1); + if (lRet <= EXCEPTION_CONTINUE_EXECUTION) + return ExceptionContinueExecution; + + /* Execute the handler (lRet >= EXCEPTION_EXECUTE_HANDLER). */ + uintptr_t const uPtrXcptHandler = uXcptHandler + pDispCtx->ImageBase; + /** @todo shouldn't we do a guard check on this call? */ + + /// @todo _NLG_Notify(uPtrXcptHandler, pXcptRegRec, 1); - debugger notification. + + RtlUnwindEx(pXcptRegRec, (void *)uPtrXcptHandler, pXcptRec, + (PVOID)(uintptr_t)pXcptRec->ExceptionCode, pCpuCtx, pDispCtx->HistoryTable); + + /// @todo _NLG_Return2(); - debugger notification. + } + } + } + } + else + { + /* + * Do unwinding. + */ + + /* Convert the target unwind address to an RVA up front for efficiency. + (I think target unwinding is what the RtlUnwindEx call above does.) */ + uint32_t const uTargetPc = pXcptRec->ExceptionFlags & EXCEPTION_TARGET_UNWIND + ? pDispCtx->TargetIp - pDispCtx->ImageBase + : UINT32_MAX; + //RTAssertMsg2("__C_specific_handler: unwind: idxScope=%#x cScopes=%#x uTargetPc=%#x fXcpt=%#x\n", idxScope, cScopes, uTargetPc, pXcptRec->ExceptionFlags); + + for (; idxScope < cScopes; idxScope++) + { + uint32_t const uBegin = pScopeTab->ScopeRecord[idxScope].BeginAddress; + uint32_t const uEnd = pScopeTab->ScopeRecord[idxScope].EndAddress; + uint32_t const cbScope = uEnd - uBegin; + if ( uRvaPc - uBegin < cbScope + && uBegin < uEnd /* paranoia */) + { + uint32_t const uFltTermHandler = pScopeTab->ScopeRecord[idxScope].HandlerAddress; + uint32_t const uXcptHandler = pScopeTab->ScopeRecord[idxScope].JumpTarget; + + /* Target unwind requires us to stop if the target PC is in the same + scope as the control PC. Happens for goto out of inner __try scope + or when longjmp'ing into a __try scope. */ + if (pXcptRec->ExceptionFlags & EXCEPTION_TARGET_UNWIND) + { + /* The scope same-ness is identified by the same handler and jump target rva values. */ + for (uint32_t idxTgtScope = 0; idxTgtScope < cScopes; idxTgtScope++) + if ( pScopeTab->ScopeRecord[idxTgtScope].JumpTarget == uXcptHandler + && pScopeTab->ScopeRecord[idxTgtScope].HandlerAddress == uFltTermHandler) + { + uint32_t const uTgtBegin = pScopeTab->ScopeRecord[idxTgtScope].BeginAddress; + uint32_t const uTgtEnd = pScopeTab->ScopeRecord[idxTgtScope].EndAddress; + uint32_t const cbTgtScope = uTgtEnd - uTgtBegin; + if ( uTargetPc - uTgtBegin < cbTgtScope + && uTgtBegin < uTgtEnd /* paranoia */) + { + //RTAssertMsg2("__C_specific_handler: ExceptionContinueSearch (#1)\n"); + return ExceptionContinueSearch; + } + } + } + + /* The unwind handlers are what we're here for. */ + if (uXcptHandler == 0) + { + PTERMINATION_HANDLER const pfnTermHandler = (PTERMINATION_HANDLER)(pDispCtx->ImageBase + uFltTermHandler); + pDispCtx->ScopeIndex = idxScope + 1; + //RTAssertMsg2("__C_specific_handler: Calling __finally %p (idxScope=%#x)\n", pfnTermHandler, idxScope); + CallFinallyFunction(pfnTermHandler, TRUE /*fAbend*/, pXcptRegRec); + } + /* Exception filter & handler entries are skipped, unless the exception + handler is being targeted by the unwind, in which case we're done + unwinding and the caller should transfer control there. */ + else if ( uXcptHandler == uTargetPc + && (pXcptRec->ExceptionFlags & EXCEPTION_TARGET_UNWIND)) + { + //RTAssertMsg2("__C_specific_handler: ExceptionContinueSearch (#2)\n"); + return ExceptionContinueSearch; + } + } + } + } + + return ExceptionContinueSearch; +} + diff --git a/src/VBox/Runtime/common/compiler/vcc/except-vcc.h b/src/VBox/Runtime/common/compiler/vcc/except-vcc.h new file mode 100644 index 00000000..f611adb6 --- /dev/null +++ b/src/VBox/Runtime/common/compiler/vcc/except-vcc.h @@ -0,0 +1,251 @@ +/* $Id: except-vcc.h $ */ +/** @file + * IPRT - Visual C++ Compiler - Exception Management. + */ + +/* + * Copyright (C) 2022-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#ifndef IPRT_INCLUDED_SRC_common_compiler_vcc_except_vcc_h +#define IPRT_INCLUDED_SRC_common_compiler_vcc_except_vcc_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#define __C_specific_handler their___C_specific_handler +#include <iprt/win/windows.h> +#undef __C_specific_handler + +#include <iprt/types.h> +#include <iprt/assertcompile.h> + + +RT_C_DECLS_BEGIN + +#if 0 +/** This is part of the AMD64 and ARM (?) exception interface, but appear to + * live in the runtime headers for some weird reason. */ +typedef enum +{ + ExceptionContinueExecution = 0, + ExceptionContinueSearch, + ExceptionNestedException, + ExceptionCollidedUnwind +} EXCEPTION_DISPOSITION; +#endif + +/* The following two are borrowed from pecoff.h, as we typically want to include + winnt.h with this header and the two header cannot co-exist. */ + +/** + * An unwind code for AMD64 and ARM64. + * + * @note Also known as UNWIND_CODE or _UNWIND_CODE. + */ +typedef union IMAGE_UNWIND_CODE +{ + struct + { + /** The prolog offset where the change takes effect. + * This means the instruction following the one being described. */ + uint8_t CodeOffset; + /** Unwind opcode. + * For AMD64 see IMAGE_AMD64_UNWIND_OP_CODES. */ + RT_GCC_EXTENSION uint8_t UnwindOp : 4; + /** Opcode specific. */ + RT_GCC_EXTENSION uint8_t OpInfo : 4; + } u; + uint16_t FrameOffset; +} IMAGE_UNWIND_CODE; +AssertCompileSize(IMAGE_UNWIND_CODE, 2); + +/** + * Unwind information for AMD64 and ARM64. + * + * Pointed to by IMAGE_RUNTIME_FUNCTION_ENTRY::UnwindInfoAddress, + * + * @note Also known as UNWIND_INFO or _UNWIND_INFO. + */ +typedef struct IMAGE_UNWIND_INFO +{ + /** Version, currently 1 or 2. The latter if IMAGE_AMD64_UWOP_EPILOG is used. */ + RT_GCC_EXTENSION uint8_t Version : 3; + /** IMAGE_UNW_FLAG_XXX */ + RT_GCC_EXTENSION uint8_t Flags : 5; + /** Size of function prolog. */ + uint8_t SizeOfProlog; + /** Number of opcodes in aOpcodes. */ + uint8_t CountOfCodes; + /** Initial frame register. */ + RT_GCC_EXTENSION uint8_t FrameRegister : 4; + /** Scaled frame register offset. */ + RT_GCC_EXTENSION uint8_t FrameOffset : 4; + /** Unwind opcodes. */ + RT_FLEXIBLE_ARRAY_EXTENSION + IMAGE_UNWIND_CODE aOpcodes[RT_FLEXIBLE_ARRAY]; +} IMAGE_UNWIND_INFO; +AssertCompileMemberOffset(IMAGE_UNWIND_INFO, aOpcodes, 4); +typedef IMAGE_UNWIND_INFO *PIMAGE_UNWIND_INFO; +typedef IMAGE_UNWIND_INFO const *PCIMAGE_UNWIND_INFO; + + + +#ifdef RT_ARCH_AMD64 +/** + * The Visual C++ 2019 layout of the GS_HANDLER_DATA data type for AMD64. + * + * This is pointed to by DISPATCHER_CONTEXT::HandlerData when dispatching + * exceptions. The data resides after the unwind info for the function. + */ +typedef struct _GS_HANDLER_DATA +{ + union + { + struct + { + uint32_t fEHandler : 1; +#define GS_HANDLER_OFF_COOKIE_IS_EHANDLER RT_BIT(0) /**< Handles exceptions. */ + uint32_t fUHandler : 1; +#define GS_HANDLER_OFF_COOKIE_IS_UHANDLER RT_BIT(1) /**< Handles unwind. */ + uint32_t fHasAlignment : 1; +#define GS_HANDLER_OFF_COOKIE_HAS_ALIGNMENT RT_BIT(2) /**< Has the uAlignmentMask member. */ + } Bits; +#define GS_HANDLER_OFF_COOKIE_MASK UINT32_C(0xfffffff8) /**< Mask to apply to offCookie to the the value. */ + uint32_t offCookie; + } u; + int32_t offAlignedBase; + /** This field is only there when GS_HANDLER_OFF_COOKIE_HAS_ALIGNMENT is set. + * it seems. */ + uint32_t uAlignmentMask; +} GS_HANDLER_DATA; +typedef GS_HANDLER_DATA *PGS_HANDLER_DATA; +typedef GS_HANDLER_DATA const *PCGS_HANDLER_DATA; +#endif /* RT_ARCH_AMD64 */ + +#ifdef RT_ARCH_X86 +void __fastcall __security_check_cookie(uintptr_t uCookieToCheck); +#else +void __cdecl __security_check_cookie(uintptr_t uCookieToCheck); +#endif + + +#ifdef RT_ARCH_X86 + +/** + * Exception registration record for _except_handler4 users + * (aka EH4_EXCEPTION_REGISTRATION_RECORD). + * + * This record is emitted immediately following the stack frame setup, i.e. + * after doing PUSH EBP and MOV EBP, ESP. So, EBP equals the address following + * this structure. + */ +typedef struct EH4_XCPT_REG_REC_T +{ + /** The saved ESP after setting up the stack frame and before the __try. */ + uintptr_t uSavedEsp; + PEXCEPTION_POINTERS pXctpPtrs; + /** The SEH exception registration record (chained). */ + EXCEPTION_REGISTRATION_RECORD XcptRec; + uintptr_t uEncodedScopeTable; + uint32_t uTryLevel; + /* uintptr_t uSavedCallerEbp; */ +} EH4_XCPT_REG_REC_T; +AssertCompileSize(EH4_XCPT_REG_REC_T, 24); +/** Pointer to an exception registration record for _except_handler4 (aka + * PEH4_EXCEPTION_REGISTRATION_RECORD). */ +typedef EH4_XCPT_REG_REC_T *PEH4_XCPT_REG_REC_T; + + +/** Exception filter function for _except_handler4 users (aka + * PEXCEPTION_FILTER_X86). */ +typedef uint32_t (__cdecl *PFN_EH4_XCPT_FILTER_T)(void); +/** Exception handler block function for _except_handler4 users (aka + * PEXCEPTION_HANDLER_X86). */ +typedef void (__cdecl *PFN_EH4_XCPT_HANDLER_T)(void); +/** Exception finally block function for _except_handler4 users (aka + * PTERMINATION_HANDLER_X86). + */ +typedef void (__fastcall *PFN_EH4_FINALLY_T)(BOOL fAbend); + +/** + * Scope table record describing __try / __except / __finally blocks (aka + * EH4_SCOPETABLE_RECORD). + */ +typedef struct EH4_SCOPE_TAB_REC_T +{ + uint32_t uEnclosingLevel; + /** Pointer to the filter sub-function if this is a __try/__except, NULL for + * __try/__finally. */ + PFN_EH4_XCPT_FILTER_T pfnFilter; + union + { + PFN_EH4_XCPT_HANDLER_T pfnHandler; + PFN_EH4_FINALLY_T pfnFinally; + }; +} EH4_SCOPE_TAB_REC_T; +AssertCompileSize(EH4_SCOPE_TAB_REC_T, 12); +/** Pointer to a const _except_handler4 scope table entry. */ +typedef EH4_SCOPE_TAB_REC_T const *PCEH4_SCOPE_TAB_REC_T; + +/** Special EH4_SCOPE_TAB_REC_T::uEnclosingLevel used to terminate the chain. */ +#define EH4_TOPMOST_TRY_LEVEL UINT32_C(0xfffffffe) + +/** + * Scope table used by _except_handler4 (aka EH4_SCOPETABLE). + */ +typedef struct EH4_SCOPE_TAB_T +{ + uint32_t offGSCookie; + uint32_t offGSCookieXor; + uint32_t offEHCookie; + uint32_t offEHCookieXor; + EH4_SCOPE_TAB_REC_T aScopeRecords[RT_FLEXIBLE_ARRAY]; +} EH4_SCOPE_TAB_T; +AssertCompileMemberOffset(EH4_SCOPE_TAB_T, aScopeRecords, 16); +/** Pointer to a const _except_handler4 scope table. */ +typedef EH4_SCOPE_TAB_T const *PCEH4_SCOPE_TAB_T; + +/** Special EH4_SCOPE_TAB_T::offGSCookie value. */ +# define EH4_NO_GS_COOKIE UINT32_C(0xfffffffe) + +#endif /* RT_ARCH_X86 */ + + + +#if defined(RT_ARCH_AMD64) +EXCEPTION_DISPOSITION __C_specific_handler(PEXCEPTION_RECORD pXcptRec, PEXCEPTION_REGISTRATION_RECORD pXcptRegRec, + PCONTEXT pCpuCtx, PDISPATCHER_CONTEXT pDispCtx); +#endif + +RT_C_DECLS_END + +#endif /* !IPRT_INCLUDED_SRC_common_compiler_vcc_except_vcc_h */ + diff --git a/src/VBox/Runtime/common/compiler/vcc/except-x86-vcc-asm.asm b/src/VBox/Runtime/common/compiler/vcc/except-x86-vcc-asm.asm new file mode 100644 index 00000000..dfc47415 --- /dev/null +++ b/src/VBox/Runtime/common/compiler/vcc/except-x86-vcc-asm.asm @@ -0,0 +1,358 @@ +; $Id: except-x86-vcc-asm.asm $ +;; @file +; IPRT - Visual C++ Compiler - x86 Exception Handler Support Code. +; + +; +; Copyright (C) 2022-2023 Oracle and/or its affiliates. +; +; This file is part of VirtualBox base platform packages, as +; available from https://www.virtualbox.org. +; +; This program is free software; you can redistribute it and/or +; modify it under the terms of the GNU General Public License +; as published by the Free Software Foundation, in version 3 of the +; License. +; +; This program is distributed in the hope that it will be useful, but +; WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; if not, see <https://www.gnu.org/licenses>. +; +; The contents of this file may alternatively be used under the terms +; of the Common Development and Distribution License Version 1.0 +; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +; in the VirtualBox distribution, in which case the provisions of the +; CDDL are applicable instead of those of the GPL. +; +; You may elect to license modified versions of this file under the +; terms and conditions of either the GPL or the CDDL or both. +; +; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +; + + + +;********************************************************************************************************************************* +;* Header Files * +;********************************************************************************************************************************* +%include "iprt/asmdefs.mac" + + +;********************************************************************************************************************************* +;* Defined Constants And Macros * +;********************************************************************************************************************************* + +;; @def WITH_NLG_STUFF +; Enabled NLG debugger hooks/whatever - no idea how it's used. +; +; So far vsdebugeng.manimpl.dll in the VS2019 remote debugger directory is the +; only component with any _NLG_ strings in it. Could not find any references +; to _NLG_ in windbg. +%define WITH_NLG_STUFF 1 + + +;********************************************************************************************************************************* +;* Structures and Typedefs * +;********************************************************************************************************************************* + +struc EH4_XCPT_REG_REC_T + .uSavedEsp resd 1 ; 0 / 0x00 + .pXctpPtrs resd 1 ; 4 / 0x04 + .XcptRec resd 2 ; 8 / 0x08 + .uEncodedScopeTable resd 1 ; 16 / 0x10 + .uTryLevel resd 1 ; 20 / 0x14 +endstruc ; 24 / 0x18 + + +;; +; Scope table record describing __try / __except / __finally blocks (aka +; EH4_SCOPETABLE_RECORD). +; +struc EH4_SCOPE_TAB_REC_T + .uEnclosingLevel resd 1 + .pfnFilter resd 1 + .pfnHandlerOrFinally resd 1 +endstruc + +;; Special EH4_SCOPE_TAB_REC_T::uEnclosingLevel used to terminate the chain. +%define EH4_TOPMOST_TRY_LEVEL 0xfffffffe + +;; +; Scope table used by _except_handler4 (aka EH4_SCOPETABLE). +; +struc EH4_SCOPE_TAB_T + .offGSCookie resd 1 + .offGSCookieXor resd 1 + .offEHCookie resd 1 + .offEHCookieXor resd 1 + .aScopeRecords resb 12 +endstruc + + +;********************************************************************************************************************************* +;* External Symbols * +;********************************************************************************************************************************* +BEGINCODE +extern IMPNAME(RtlUnwind@16) +extern _rtVccEh4DoLocalUnwindHandler@16 + + +;********************************************************************************************************************************* +;* Global Variables * +;********************************************************************************************************************************* + +;; Delcare rtVccEh4DoLocalUnwindHandler() in except-x86.cpp as a save exception handler. +; This adds the symbol table number of the exception handler to the special .sxdata section. +safeseh _rtVccEh4DoLocalUnwindHandler@16 + +%ifdef WITH_NLG_STUFF +BEGINDATA +GLOBALNAME_RAW __NLG_Destination, data, hidden + dd 019930520h + dd 0 + dd 0 + dd 0 +%endif + + +BEGINCODE + +%ifdef WITH_NLG_STUFF + +;; +; Some VS debugger interface, I think. +; +; @param EDX Notification code. +; @uses EBP, EAX & ECX are preserved. This differs from other implementation, +; but simplifies the calling code. +; +ALIGNCODE(32) +GLOBALNAME_RAW __NLG_Notify, function, hidden + ; + ; Save registers. + ; + push eax + push ecx + push ebp + push ebx + + + ; + ; ECX = notification code. + ; EBX = __NLG_Destination + ; + mov ecx, edx ; Originally held in ECX, so moving it there for now. + mov ebx, __NLG_Destination + +__NLG_Go: + ; + ; Save info to __NLG_Destination and the stack (same layout). + ; + mov [ebx + 0ch], ebp + push ebp + mov [ebx + 08h], ecx + push ecx + mov [ebx + 04h], eax + push eax + + ; + ; This is presumably the symbol the debugger hooks on to as the string + ; "__NLG_Dispatch" was found in vsdebugeng.manimpl.dll in VS2019. + ; +global __NLG_Dispatch +__NLG_Dispatch: + + ; Drop the info structure from the stack. + add esp, 4*3 + + ; + ; Restore registers and drop the parameter as we return. + ; + ; Note! This may sabotage attempts by the debugger to modify the state... + ; But that can be fixed once we know this is a requirement. Just + ; keep things simple for the caller for now. + ; + pop ebx + pop ebp + pop ecx + pop eax + ret + +;; +; NLG call + return2. +; +; The "__NLG_Return2" symbol was observed in several vsdebugeng.manimpl.dll +; strings in VS2019. +; +ALIGNCODE(4) +GLOBALNAME_RAW __NLG_Call, function, hidden + call eax + +global __NLG_Return2 +__NLG_Return2: + ret + +%endif + + +;; +; Calls the filter sub-function for a __finally statement. +; +; This sets all GRPs to zero, except for ESP, EBP and EAX. +; +; @param pfnFilter [ebp + 08h] +; @param fAbend [ebp + 0ch] +; @param pbFrame [ebp + 10h] +; +ALIGNCODE(64) +BEGINPROC rtVccEh4DoFinally + push ebp + mov ebp, esp + push ebx + push edi + push esi + + xor edi, edi + xor esi, esi +%ifndef WITH_NLG_STUFF + xor edx, edx +%endif + xor ebx, ebx + + mov eax, [ebp + 08h] ; pfnFilter + movzx ecx, byte [ebp + 0ch] ; fAbend + mov ebp, [ebp + 10h] ; pbFrame + +%ifdef WITH_NLG_STUFF + mov edx, 101h + call __NLG_Notify + xor edx, edx + + call __NLG_Call +%else + call eax +%endif + + pop esi + pop edi + pop ebx + pop ebp + ret +ENDPROC rtVccEh4DoFinally + + +;; +; Calls the filter sub-function for an __except statement. +; +; This sets all GRPs to zero, except for ESP, EBP and ECX. +; +; @param pfnFilter [ebp + 08h] +; @param pbFrame [ebp + 0ch] +; +ALIGNCODE(32) +BEGINPROC rtVccEh4DoFiltering + push ebp + push ebx + push edi + push esi + + mov ecx, [esp + 5 * 4 + 0] ; pfnFilter + mov ebp, [esp + 5 * 4 + 4] ; pbFrame + + xor edi, edi + xor esi, esi + xor edx, edx + xor ebx, ebx + xor eax, eax + + call ecx + + pop esi + pop edi + pop ebx + pop ebp + ret +ENDPROC rtVccEh4DoFiltering + + +;; +; Resumes executing in an __except block (never returns). +; +; @param pfnHandler [ebp + 08h] +; @param pbFrame [ebp + 0ch] +; +ALIGNCODE(32) +BEGINPROC rtVccEh4JumpToHandler + ; + ; Since we're never returning there is no need to save anything here. So, + ; just start by loading parameters into registers. + ; + mov esi, [esp + 1 * 4 + 0] ; pfnFilter + mov ebp, [esp + 1 * 4 + 4] ; pbFrame + +%ifdef WITH_NLG_STUFF + ; + ; Notify VS debugger/whatever. + ; + mov eax, esi + mov edx, 1 + call __NLG_Notify +%endif + + ; + ; Zero all GPRs except for ESP, EBP and ESI. + ; + xor edi, edi + xor edx, edx + xor ecx, ecx + xor ebx, ebx + xor eax, eax + + jmp esi +ENDPROC rtVccEh4JumpToHandler + + +;; +; This does global unwinding via RtlUnwind. +; +; The interface kind of requires us to do this from assembly. +; +; @param pXcptRec [ebp + 08h] +; @param pXcptRegRec [ebp + 0ch] +; +ALIGNCODE(32) +BEGINPROC rtVccEh4DoGlobalUnwind + push ebp + mov ebp, esp + + ; Save all non-volatile registers. + push edi + push esi + push ebx + + ; + ; Call unwind function. + ; + push 0 ; ReturnValue + push dword [ebp + 08h] ; ExceptionRecord - pXcptRec + push .return ; TargetIp + push dword [ebp + 0ch] ; TargetFrame - pXcptRegRec + call IMP(RtlUnwind@16) + + ; + ; The RtlUnwind code will place us here if it doesn't return the regular way. + ; +.return: + ; Restore non-volatile registers. + pop ebx + pop esi + pop edi + + leave + ret +ENDPROC rtVccEh4DoGlobalUnwind + diff --git a/src/VBox/Runtime/common/compiler/vcc/except-x86-vcc.cpp b/src/VBox/Runtime/common/compiler/vcc/except-x86-vcc.cpp new file mode 100644 index 00000000..17dd3a0d --- /dev/null +++ b/src/VBox/Runtime/common/compiler/vcc/except-x86-vcc.cpp @@ -0,0 +1,329 @@ +/* $Id: except-x86-vcc.cpp $ */ +/** @file + * IPRT - Visual C++ Compiler - x86 Exception Handler Filter. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/nt/nt-and-windows.h> + +#include "internal/compiler-vcc.h" +#include "except-vcc.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Extended exception registration record used by rtVccEh4DoLocalUnwind + * and rtVccEh4DoLocalUnwindHandler. + */ +typedef struct EH4_LOCAL_UNWIND_XCPT_REG +{ + /** Security cookie. */ + uintptr_t uEHCookieFront; + /** The actual registration record. */ + EXCEPTION_REGISTRATION_RECORD XcptRegRec; + /** @name rtVccEh4DoLocalUnwind parameters + * @{ */ + PEH4_XCPT_REG_REC_T pEh4XcptRegRec; + uint32_t uTargetTryLevel; + uint8_t const *pbFrame; + /** @} */ + /** Security cookie. */ + uintptr_t uEHCookieBack; +} EH4_LOCAL_UNWIND_XCPT_REG; + + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +extern "C" uintptr_t __security_cookie; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +DECLASM(LONG) rtVccEh4DoFiltering(PFN_EH4_XCPT_FILTER_T pfnFilter, uint8_t const *pbFrame); +DECLASM(DECL_NO_RETURN(void)) rtVccEh4JumpToHandler(PFN_EH4_XCPT_HANDLER_T pfnHandler, uint8_t const *pbFrame); +DECLASM(void) rtVccEh4DoGlobalUnwind(PEXCEPTION_RECORD pXcptRec, PEXCEPTION_REGISTRATION_RECORD pXcptRegRec); +DECLASM(void) rtVccEh4DoFinally(PFN_EH4_FINALLY_T pfnFinally, bool fAbend, uint8_t const *pbFrame); +extern "C" EXCEPTION_DISPOSITION __stdcall +rtVccEh4DoLocalUnwindHandler(PEXCEPTION_RECORD pXcptRec, PVOID pvEstFrame, PCONTEXT pCpuCtx, PVOID pvDispCtx); + + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4733) /* warning C4733: Inline asm assigning to 'FS:0': handler not registered as safe handler */ +#endif + +/** + * Calls the __finally blocks up to @a uTargetTryLevel is reached, starting with + * @a pEh4XcptRegRec->uTryLevel. + * + * @param pEh4XcptRegRec The EH4 exception registration record. + * @param uTargetTryLevel The target __try level to stop unwinding at. + * @param pbFrame The frame pointer (EBP). + */ +static void rtVccEh4DoLocalUnwind(PEH4_XCPT_REG_REC_T pEh4XcptRegRec, uint32_t uTargetTryLevel, uint8_t const *pbFrame) +{ + /* + * Manually set up exception handler. + */ + EH4_LOCAL_UNWIND_XCPT_REG RegRec = + { + __security_cookie ^ (uintptr_t)&RegRec, + { + (EXCEPTION_REGISTRATION_RECORD *)__readfsdword(RT_UOFFSETOF(NT_TIB, ExceptionList)), + rtVccEh4DoLocalUnwindHandler /* safeseh (.sxdata) entry emitted by except-x86-vcc-asm.asm */ + }, + pEh4XcptRegRec, + uTargetTryLevel, + pbFrame, + __security_cookie ^ (uintptr_t)&RegRec + }; + __writefsdword(RT_UOFFSETOF(NT_TIB, ExceptionList), (uintptr_t)&RegRec.XcptRegRec); + + /* + * Do the unwinding. + */ + uint32_t uCurTryLevel = pEh4XcptRegRec->uTryLevel; + while ( uCurTryLevel != EH4_TOPMOST_TRY_LEVEL + && ( uCurTryLevel > uTargetTryLevel + || uTargetTryLevel == EH4_TOPMOST_TRY_LEVEL /* if we knew what 0xffffffff meant, this could probably be omitted */ )) + { + PCEH4_SCOPE_TAB_T const pScopeTable = (PCEH4_SCOPE_TAB_T)(pEh4XcptRegRec->uEncodedScopeTable ^ __security_cookie); + PCEH4_SCOPE_TAB_REC_T const pEntry = &pScopeTable->aScopeRecords[uCurTryLevel]; + + pEh4XcptRegRec->uTryLevel = uCurTryLevel = pEntry->uEnclosingLevel; + + /* __finally scope table entries have no filter sub-function. */ + if (!pEntry->pfnFilter) + { + //RTAssertMsg2("rtVccEh4DoLocalUnwind: Calling %p (level %#x)\n", pEntry->pfnFinally, uCurTryLevel); + rtVccEh4DoFinally(pEntry->pfnFinally, true /*fAbend*/, pbFrame); + + /* Read the try level again in case it changed... */ + uCurTryLevel = pEh4XcptRegRec->uTryLevel; + } + } + + /* + * Deregister exception handler. + */ + __writefsdword(RT_UOFFSETOF(NT_TIB, ExceptionList), (uintptr_t)RegRec.XcptRegRec.Next); +} + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +/** + * Exception handler for rtVccEh4DoLocalUnwind. + */ +EXCEPTION_DISPOSITION __stdcall +rtVccEh4DoLocalUnwindHandler(PEXCEPTION_RECORD pXcptRec, PVOID pvEstFrame, PCONTEXT pCpuCtx, PVOID pvDispCtx) +{ + EH4_LOCAL_UNWIND_XCPT_REG *pMyRegRec = RT_FROM_MEMBER(pvEstFrame, EH4_LOCAL_UNWIND_XCPT_REG, XcptRegRec); + __security_check_cookie(pMyRegRec->uEHCookieFront ^ (uintptr_t)pMyRegRec); + __security_check_cookie(pMyRegRec->uEHCookieBack ^ (uintptr_t)pMyRegRec); + + /* + * This is a little sketchy as it isn't all that well documented by the OS + * vendor, but if invoked while unwinding, we return ExceptionCollidedUnwind + * and update the *ppDispCtx value to point to the colliding one. + */ + if (pXcptRec->ExceptionFlags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND)) + { + rtVccEh4DoLocalUnwind(pMyRegRec->pEh4XcptRegRec, pMyRegRec->uTargetTryLevel, pMyRegRec->pbFrame); + + PEXCEPTION_REGISTRATION_RECORD *ppDispCtx = (PEXCEPTION_REGISTRATION_RECORD *)pvDispCtx; + *ppDispCtx = &pMyRegRec->XcptRegRec; + return ExceptionCollidedUnwind; + } + + /* + * In all other cases we do nothing special. + */ + RT_NOREF(pCpuCtx); + return ExceptionContinueSearch; +} + + +/** + * This validates the CPU context, may terminate the application if invalid. + */ +DECLINLINE(void) rtVccValidateExceptionContextRecord(PCONTEXT pCpuCtx) +{ + if (RT_LIKELY( !rtVccIsGuardICallChecksActive() + || rtVccIsPointerOnTheStack(pCpuCtx->Esp))) + { /* likely */ } + else + rtVccCheckContextFailed(pCpuCtx); +} + + +/** + * Helper that validates stack cookies. + */ +DECLINLINE(void) rtVccEh4ValidateCookies(PCEH4_SCOPE_TAB_T pScopeTable, uint8_t const *pbFrame) +{ + if (pScopeTable->offGSCookie != EH4_NO_GS_COOKIE) + { + uintptr_t uGsCookie = *(uintptr_t const *)&pbFrame[pScopeTable->offGSCookie]; + uGsCookie ^= (uintptr_t)&pbFrame[pScopeTable->offGSCookieXor]; + __security_check_cookie(uGsCookie); + } + + uintptr_t uEhCookie = *(uintptr_t const *)&pbFrame[pScopeTable->offEHCookie]; + uEhCookie ^= (uintptr_t)&pbFrame[pScopeTable->offEHCookieXor]; + __security_check_cookie(uEhCookie); +} + + +/** + * Call exception filters, handlers and unwind code for x86 code. + * + * This is called for windows' structured exception handling (SEH) in x86 32-bit + * code, i.e. the __try/__except/__finally stuff in Visual C++. The compiler + * generate scope records for the __try/__except blocks as well as unwind + * records for __finally and probably C++ stack object destructors. + * + * @returns Exception disposition. + * @param pXcptRec The exception record. + * @param pXcptRegRec The exception registration record, taken to be the frame + * address. + * @param pCpuCtx The CPU context for the exception. + * @param pDispCtx Dispatcher context. + */ +extern "C" __declspec(guard(suppress)) +DWORD _except_handler4(PEXCEPTION_RECORD pXcptRec, PEXCEPTION_REGISTRATION_RECORD pXcptRegRec, PCONTEXT pCpuCtx, PVOID pvCtx) +{ + /* + * The registration record (probably chained on FS:[0] like in the OS/2 days) + * points to a larger structure specific to _except_handler4. The structure + * is planted right after the saved caller EBP value when establishing the + * stack frame, so EBP = pXcptRegRec + 1; + */ + PEH4_XCPT_REG_REC_T const pEh4XcptRegRec = RT_FROM_MEMBER(pXcptRegRec, EH4_XCPT_REG_REC_T, XcptRec); + uint8_t * const pbFrame = (uint8_t *)&pEh4XcptRegRec[1]; + PCEH4_SCOPE_TAB_T const pScopeTable = (PCEH4_SCOPE_TAB_T)(pEh4XcptRegRec->uEncodedScopeTable ^ __security_cookie); + + /* + * Validate the stack cookie and exception context. + */ + rtVccEh4ValidateCookies(pScopeTable, pbFrame); + rtVccValidateExceptionContextRecord(pCpuCtx); + + /* + * If dispatching an exception, call the exception filter functions and jump + * to the __except blocks if so directed. + */ + if (IS_DISPATCHING(pXcptRec->ExceptionFlags)) + { + uint32_t uTryLevel = pEh4XcptRegRec->uTryLevel; + //RTAssertMsg2("_except_handler4: dispatch: uTryLevel=%#x\n", uTryLevel); + while (uTryLevel != EH4_TOPMOST_TRY_LEVEL) + { + PCEH4_SCOPE_TAB_REC_T const pEntry = &pScopeTable->aScopeRecords[uTryLevel]; + PFN_EH4_XCPT_FILTER_T const pfnFilter = pEntry->pfnFilter; + if (pfnFilter) + { + /* Call the __except filtering expression: */ + //RTAssertMsg2("_except_handler4: Calling pfnFilter=%p\n", pfnFilter); + EXCEPTION_POINTERS XcptPtrs = { pXcptRec, pCpuCtx }; + pEh4XcptRegRec->pXctpPtrs = &XcptPtrs; + LONG lRet = rtVccEh4DoFiltering(pfnFilter, pbFrame); + pEh4XcptRegRec->pXctpPtrs = NULL; + //RTAssertMsg2("_except_handler4: pfnFilter=%p -> %ld\n", pfnFilter, lRet); + rtVccEh4ValidateCookies(pScopeTable, pbFrame); + + /* Return if we're supposed to continue execution (the convention + it to match negative values rather than the exact defined value): */ + AssertCompile(EXCEPTION_CONTINUE_EXECUTION == -1); + if (lRet <= EXCEPTION_CONTINUE_EXECUTION) + return ExceptionContinueExecution; + + /* Similarly, the handler is executed for any positive value. */ + AssertCompile(EXCEPTION_CONTINUE_SEARCH == 0); + AssertCompile(EXCEPTION_EXECUTE_HANDLER == 1); + if (lRet >= EXCEPTION_EXECUTE_HANDLER) + { + /* We're about to resume execution in the __except block, so unwind + up to it first. */ + //RTAssertMsg2("_except_handler4: global unwind\n"); + rtVccEh4DoGlobalUnwind(pXcptRec, &pEh4XcptRegRec->XcptRec); + if (pEh4XcptRegRec->uTryLevel != EH4_TOPMOST_TRY_LEVEL) + { + //RTAssertMsg2("_except_handler4: local unwind\n"); + rtVccEh4DoLocalUnwind(pEh4XcptRegRec, uTryLevel, pbFrame); + } + rtVccEh4ValidateCookies(pScopeTable, pbFrame); + + /* Now jump to the __except block. This will _not_ return. */ + //RTAssertMsg2("_except_handler4: jumping to __except block %p (level %#x)\n", pEntry->pfnHandler, pEntry->uEnclosingLevel); + pEh4XcptRegRec->uTryLevel = pEntry->uEnclosingLevel; + rtVccEh4ValidateCookies(pScopeTable, pbFrame); /* paranoia^2 */ + + rtVccEh4JumpToHandler(pEntry->pfnHandler, pbFrame); + /* (not reached) */ + } + } + + /* + * Next try level. + */ + uTryLevel = pEntry->uEnclosingLevel; + } + } + /* + * If not dispatching we're unwinding, so we call any __finally blocks. + */ + else + { + //RTAssertMsg2("_except_handler4: unwind: uTryLevel=%#x\n", pEh4XcptRegRec->uTryLevel); + if (pEh4XcptRegRec->uTryLevel != EH4_TOPMOST_TRY_LEVEL) + { + rtVccEh4DoLocalUnwind(pEh4XcptRegRec, EH4_TOPMOST_TRY_LEVEL, pbFrame); + rtVccEh4ValidateCookies(pScopeTable, pbFrame); + } + } + + RT_NOREF(pvCtx); + return ExceptionContinueSearch; +} + diff --git a/src/VBox/Runtime/common/compiler/vcc/fltused-vcc.cpp b/src/VBox/Runtime/common/compiler/vcc/fltused-vcc.cpp new file mode 100644 index 00000000..d26f2b90 --- /dev/null +++ b/src/VBox/Runtime/common/compiler/vcc/fltused-vcc.cpp @@ -0,0 +1,58 @@ +/* $Id: fltused-vcc.cpp $ */ +/** @file + * IPRT - No-CRT - Basic allocators, Windows. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "internal/iprt.h" +#include "internal/compiler-vcc.h" + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Very like some remnant from the 8086, 286 and 386 days of DOS, OS/2 and + * similar, where you could link with different floating point libs. My guess + * would be that this constant indicates to the compiled code which floating + * point support the library provides, probably especially as it comes to + * printf and scanf conversion but probably also emulation/real hw. + * + * Found some old 16-bit and 32-bit MSC C libraries (probably around v6.0) + * which all seems to define it as 0x9876. They also have a whole bunch of + * external dependencies on what seems to be mostly conversion helpers. + */ +unsigned _fltused = 0x9875; diff --git a/src/VBox/Runtime/common/compiler/vcc/ftol2-vcc.asm b/src/VBox/Runtime/common/compiler/vcc/ftol2-vcc.asm new file mode 100644 index 00000000..40bd3fa2 --- /dev/null +++ b/src/VBox/Runtime/common/compiler/vcc/ftol2-vcc.asm @@ -0,0 +1,80 @@ +; $Id: ftol2-vcc.asm $ +;; @file +; IPRT - Floating Point to Integer related Visual C++ support routines. +; + +; +; Copyright (C) 2022-2023 Oracle and/or its affiliates. +; +; This file is part of VirtualBox base platform packages, as +; available from https://www.virtualbox.org. +; +; This program is free software; you can redistribute it and/or +; modify it under the terms of the GNU General Public License +; as published by the Free Software Foundation, in version 3 of the +; License. +; +; This program is distributed in the hope that it will be useful, but +; WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; if not, see <https://www.gnu.org/licenses>. +; +; The contents of this file may alternatively be used under the terms +; of the Common Development and Distribution License Version 1.0 +; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +; in the VirtualBox distribution, in which case the provisions of the +; CDDL are applicable instead of those of the GPL. +; +; You may elect to license modified versions of this file under the +; terms and conditions of either the GPL or the CDDL or both. +; +; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +; + + +%include "iprt/asmdefs.mac" +%include "iprt/x86.mac" + + +;; +; Convert st0 to integer returning it in eax, popping st0. +; +; @returns value in eax +; @param st0 The value to convet. Will be popped. +; @uses eax, st0, FSW.TOP +; +GLOBALNAME_RAW __ftol2_sse_excpt, function, RT_NOTHING +GLOBALNAME_RAW __ftol2_sse, function, RT_NOTHING ;; @todo kind of expect __ftol2_sse to take input in xmm0 and return in edx:eax. +BEGINPROC_RAW __ftoi2 + push ebp + mov ebp, esp + sub esp, 8h + fisttp dword [esp] + mov eax, [esp] + leave + ret +ENDPROC_RAW __ftoi2 + + +;; +; Convert st0 to integer returning it in edx:eax, popping st0. +; +; @returns value in edx:eax +; @param st0 The value to convet. Will be popped. +; @uses eax, edx, st0, FSW.TOP +; +BEGINPROC_RAW __ftol2 + push ebp + mov ebp, esp + sub esp, 8h + and esp, 0fffffff8h ; proper alignment. + fisttp qword [esp] + mov eax, [esp] + mov edx, [esp + 4] + leave + ret +ENDPROC_RAW __ftol2 + diff --git a/src/VBox/Runtime/common/compiler/vcc/guard-vcc.asm b/src/VBox/Runtime/common/compiler/vcc/guard-vcc.asm new file mode 100644 index 00000000..42e6d5a8 --- /dev/null +++ b/src/VBox/Runtime/common/compiler/vcc/guard-vcc.asm @@ -0,0 +1,108 @@ +; $Id: guard-vcc.asm $ +;; @file +; IPRT - Control Flow Guard related Visual C++ support routines. +; + +; +; Copyright (C) 2022-2023 Oracle and/or its affiliates. +; +; This file is part of VirtualBox base platform packages, as +; available from https://www.virtualbox.org. +; +; This program is free software; you can redistribute it and/or +; modify it under the terms of the GNU General Public License +; as published by the Free Software Foundation, in version 3 of the +; License. +; +; This program is distributed in the hope that it will be useful, but +; WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; if not, see <https://www.gnu.org/licenses>. +; +; The contents of this file may alternatively be used under the terms +; of the Common Development and Distribution License Version 1.0 +; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +; in the VirtualBox distribution, in which case the provisions of the +; CDDL are applicable instead of those of the GPL. +; +; You may elect to license modified versions of this file under the +; terms and conditions of either the GPL or the CDDL or both. +; +; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +; + + + +;********************************************************************************************************************************* +;* Header Files * +;********************************************************************************************************************************* +%include "iprt/asmdefs.mac" +%include "iprt/x86.mac" + + + +;********************************************************************************************************************************* +;* Global Variables * +;********************************************************************************************************************************* + +; +; Points to a NOP icall checker by default. +; The loader will replace this when we start advertising it in the load config. +; +section .0000cfg rdata align=8 + +GLOBALNAME __guard_check_icall_fptr + RTCCPTR_DEF NAME(__guard_check_icall_nop) + +%ifdef RT_ARCH_AMD64 +GLOBALNAME __guard_dispatch_icall_fptr + RTCCPTR_DEF __guard_dispatch_icall_nop + +; xfg stuff (haven't seen it generated or used yet). +GLOBALNAME __guard_xfg_check_icall_fptr + RTCCPTR_DEF __guard_check_icall_nop + +GLOBALNAME __guard_xfg_dispatch_icall_fptr + RTCCPTR_DEF __guard_xfg_dispatch_icall_nop + +GLOBALNAME __guard_xfg_table_dispatch_icall_fptr + RTCCPTR_DEF __guard_xfg_dispatch_icall_nop + +%endif + + +BEGINCODE +;; +; Check that doesn't do anything. +; +; This is for older windows versions which doesn't support call flow guard stuff. +; +BEGINPROC __guard_check_icall_nop + ret +ENDPROC __guard_check_icall_nop + +%ifdef RT_ARCH_AMD64 +;; +; Target function in RAX +; +; This is for older windows versions which doesn't support call flow guard stuff. +; +BEGINPROC __guard_dispatch_icall_nop + jmp rax +ENDPROC __guard_dispatch_icall_nop +%endif + +%ifdef RT_ARCH_AMD64 +;; +; Target function in RAX +; +; This is for windows versions which doesn't support extended call flow guard stuff. +; +BEGINPROC __guard_xfg_dispatch_icall_nop + jmp [__guard_dispatch_icall_nop wrt RIP] +ENDPROC __guard_xfg_dispatch_icall_nop +%endif + diff --git a/src/VBox/Runtime/common/compiler/vcc/initializers-c-cpp-vcc.cpp b/src/VBox/Runtime/common/compiler/vcc/initializers-c-cpp-vcc.cpp new file mode 100644 index 00000000..0fc451db --- /dev/null +++ b/src/VBox/Runtime/common/compiler/vcc/initializers-c-cpp-vcc.cpp @@ -0,0 +1,158 @@ +/* $Id: initializers-c-cpp-vcc.cpp $ */ +/** @file + * IPRT - Visual C++ Compiler - C & C++ Initializers and Terminators. + */ + +/* + * Copyright (C) 2022-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define IPRT_COMPILER_VCC_WITH_C_INIT_TERM_SECTIONS +#define IPRT_COMPILER_VCC_WITH_CPP_INIT_SECTIONS +#include "internal/compiler-vcc.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef void (__cdecl *PFNVCINITTERM)(void); +typedef int (__cdecl *PFNVCINITTERMRET)(void); + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** @name Initializer arrays. + * + * The important thing here are the section names, the linker wi. + * + * @{ */ +/** Start of the C initializer array. */ +__declspec(allocate(".CRT$XIA")) PFNVCINITTERMRET g_apfnRTVccInitializers_C_Start = NULL; +/** End of the C initializer array. */ +__declspec(allocate(".CRT$XIZ")) PFNVCINITTERMRET g_apfnRTVccInitializers_C_End = NULL; + +/** Start of the C pre-terminator array. */ +__declspec(allocate(".CRT$XPA")) PFNVCINITTERM g_apfnRTVccEarlyTerminators_C_Start = NULL; +/** End of the C pre-terminator array. */ +__declspec(allocate(".CRT$XPZ")) PFNVCINITTERM g_apfnRTVccEarlyTerminators_C_End = NULL; + +/** Start of the C terminator array. */ +__declspec(allocate(".CRT$XTA")) PFNVCINITTERM g_apfnRTVccTerminators_C_Start = NULL; +/** End of the C terminator array. */ +__declspec(allocate(".CRT$XTZ")) PFNVCINITTERM g_apfnRTVccTerminators_C_End = NULL; + +/** Start of the C++ initializer array. */ +__declspec(allocate(".CRT$XCA")) PFNVCINITTERM g_apfnRTVccInitializers_Cpp_Start = NULL; +/** End of the C++ initializer array. */ +__declspec(allocate(".CRT$XCZ")) PFNVCINITTERM g_apfnRTVccInitializers_Cpp_End = NULL; + + +/* Tell the linker to merge the .CRT* sections into .rdata */ +#pragma comment(linker, "/merge:.CRT=.rdata ") +/** @} */ + + +/** + * Runs the C and C++ initializers. + * + * @returns 0 on success, non-zero return from C initalizer on failure. + */ +int rtVccInitializersRunInit(void) +{ + /* + * Run the C initializers first. + */ + for (PFNVCINITTERMRET *ppfn = &g_apfnRTVccInitializers_C_Start; + (uintptr_t)ppfn < (uintptr_t)&g_apfnRTVccInitializers_C_End; + ppfn++) + { + PFNVCINITTERMRET const pfn = *ppfn; + if (pfn) + { + int const rc = pfn(); + if (RT_LIKELY(rc == 0)) + { /* likely */ } + else + return rc; + } + } + + /* + * Run the C++ initializers. + */ + for (PFNVCINITTERM *ppfn = &g_apfnRTVccInitializers_Cpp_Start; + (uintptr_t)ppfn < (uintptr_t)&g_apfnRTVccInitializers_Cpp_End; + ppfn++) + { + PFNVCINITTERM const pfn = *ppfn; + if (pfn) + pfn(); + } + + return 0; +} + + +/** + * Runs the C terminator callbacks. + */ +void rtVccInitializersRunTerm(void) +{ + /* + * First the early terminators. + */ + for (PFNVCINITTERM *ppfn = &g_apfnRTVccEarlyTerminators_C_Start; + (uintptr_t)ppfn < (uintptr_t)&g_apfnRTVccEarlyTerminators_C_End; + ppfn++) + { + PFNVCINITTERM const pfn = *ppfn; + if (pfn) + pfn(); + } + + /* + * Then the real terminator list. + */ + for (PFNVCINITTERM *ppfn = &g_apfnRTVccTerminators_C_Start; + (uintptr_t)ppfn < (uintptr_t)&g_apfnRTVccTerminators_C_End; + ppfn++) + { + PFNVCINITTERM const pfn = *ppfn; + if (pfn) + pfn(); + } + +} + diff --git a/src/VBox/Runtime/common/compiler/vcc/loadcfg-vcc.c b/src/VBox/Runtime/common/compiler/vcc/loadcfg-vcc.c new file mode 100644 index 00000000..c213db19 --- /dev/null +++ b/src/VBox/Runtime/common/compiler/vcc/loadcfg-vcc.c @@ -0,0 +1,154 @@ +/* $Id: loadcfg-vcc.c $ */ +/** @file + * IPRT - Visual C++ Compiler - PE/Windows Load Configuration. + * + * @note This is a C file and not C++ because the compiler generates fixups + * that upsets the linker (ADDR32 + ADDR32_4 instead of ADDR64). + */ + +/* + * Copyright (C) 2022-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "internal/iprt.h" +#include <iprt/formats/pecoff.h> + +#include "internal/compiler-vcc.h" + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +extern uintptr_t __security_cookie; /**< nocrt-stack-win.asm */ +#ifdef RT_ARCH_X86 +extern uintptr_t __safe_se_handler_table[]; /**< Linker generated. */ +extern uint8_t __safe_se_handler_count; /**< Absolute "address" defined by the linker representing the table size. */ +#endif +#ifdef RT_ARCH_AMD64 +extern uintptr_t __guard_dispatch_icall_fptr;/**< nocrt-guard-win.asm */ +#endif +extern uintptr_t __guard_fids_table[]; /**< Linker generated. */ +extern uint8_t __guard_fids_count; /** Another absolute "address" generated by the linker for a table size. */ +extern uint8_t __guard_flags; /** Another absolute "address" generated by the linker, flags value this time. */ +extern uintptr_t __guard_iat_table[]; /**< Linker generated. */ +extern uint8_t __guard_iat_count; /** Another absolute "address" generated by the linker for a table size. */ +extern uintptr_t __guard_longjmp_table[]; /**< Linker generated. */ +extern uint8_t __guard_longjmp_count; /** Another absolute "address" generated by the linker for a table size. */ +extern uint8_t __enclave_config; /** Another absolute "address" generated by the linker, flags value this time. */ +extern uintptr_t __guard_eh_cont_table[]; /**< Linker generated. */ +extern uint8_t __guard_eh_cont_count; /** Another absolute "address" generated by the linker for a table size. */ +#ifdef RT_ARCH_AMD64 +extern uintptr_t __guard_xfg_check_icall_fptr; /**< nocrt-guard-win.asm */ +extern uintptr_t __guard_xfg_dispatch_icall_fptr; /**< nocrt-guard-win.asm */ +extern uintptr_t __guard_xfg_table_dispatch_icall_fptr; /**< nocrt-guard-win.asm */ +#endif + + +/** + * The load configuration for the PE image. + * + * The name of this is dictated by the linker, as it looks for a + * _load_config_used symbol and puts it's address and (somehow) size in the load + * config data dir entry. + * + * @todo use _MSC_VER to reduce this for older linkers which doesn't support all + * the machinactions we include here for the 2019 (14.29.30139.0) linker. + */ +RT_CONCAT(IMAGE_LOAD_CONFIG_DIRECTORY, ARCH_BITS) const _load_config_used = +{ + /* .Size = */ sizeof(_load_config_used), + /* .TimeDateStamp = */ 0, + /* .MajorVersion = */ 0, + /* .MinorVersion = */ 0, + /* .GlobalFlagsClear = */ 0, + /* .GlobalFlagsSet = */ 0, + /* .CriticalSectionDefaultTimeout = */ 0, + /* .DeCommitFreeBlockThreshold = */ 0, + /* .DeCommitTotalFreeThreshold = */ 0, + /* .LockPrefixTable = */ 0, + /* .MaximumAllocationSize = */ 0, + /* .VirtualMemoryThreshold = */ 0, + /* .ProcessHeapFlags = */ 0, + /* .ProcessAffinityMask = */ 0, + /* .CSDVersion = */ 0, + /* .DependentLoadFlags = */ 0, + /* .EditList = */ 0, + /* .SecurityCookie = */ (uintptr_t)&__security_cookie, +#ifdef RT_ARCH_X86 + /* .SEHandlerTable = */ (uintptr_t)&__safe_se_handler_table[0], + /* .SEHandlerCount = */ (uintptr_t)&__safe_se_handler_count, /* Absolute "address", remember. */ +#else + /* .SEHandlerTable = */ 0, + /* .SEHandlerCount = */ 0, +#endif + /* .GuardCFCCheckFunctionPointer = */ (uintptr_t)&__guard_check_icall_fptr, +#ifdef RT_ARCH_AMD64 + /* .GuardCFDispatchFunctionPointer = */ (uintptr_t)&__guard_dispatch_icall_fptr, +#else + /* .GuardCFDispatchFunctionPointer = */ 0, +#endif + /* .GuardCFFunctionTable = */ (uintptr_t)&__guard_fids_table[0], + /* .GuardCFFunctionCount = */ (uintptr_t)&__guard_fids_count, /* Absolute "address", remember. */ + /* .GuardFlags = */ (uint32_t)(uintptr_t)&__guard_flags, /* Ditto. */ + /* .CodeIntegrity = */ { 0, 0, 0, 0 }, + /* .GuardAddressTakenIatEntryTable = */ (uintptr_t)__guard_iat_table, + /* .GuardAddressTakenIatEntryCount = */ (uintptr_t)&__guard_iat_count, /* The same. */ + /* .GuardLongJumpTargetTable = */ (uintptr_t)&__guard_longjmp_table[0], + /* .GuardLongJumpTargetCount = */ (uintptr_t)&__guard_longjmp_count, /* Another one. */ + /* .DynamicValueRelocTable = */ 0, + /* .CHPEMetadataPointer = */ 0, /** @todo ARM */ + /* .GuardRFFailureRoutine = */ 0, + /* .GuardRFFailureRoutineFunctionPointer= */ 0, + /* .DynamicValueRelocTableOffset = */ 0, + /* .DynamicValueRelocTableSection = */ 0, + /* .Reserved2 = */ 0, + /* .GuardRFVerifyStackPointerFunctionPointer=*/ 0, + /* .HotPatchTableOffset = */ 0, + /* .Reserved3 = */ 0, + /* .EnclaveConfigurationPointer = */ (uintptr_t)&__enclave_config, /* And another one. */ + /* .VolatileMetadataPointer = */ 0, + /* .GuardEHContinuationTable = */ (uintptr_t)&__guard_eh_cont_table[0], + /* .GuardEHContinuationCount = */ (uintptr_t)&__guard_eh_cont_count, /* Yet another one. */ +#ifdef RT_ARCH_AMD64 + /* .GuardXFGCheckFunctionPointer = */ (uintptr_t)&__guard_xfg_check_icall_fptr, + /* .GuardXFGDispatchFunctionPointer = */ (uintptr_t)&__guard_xfg_dispatch_icall_fptr, + /* .GuardXFGTableDispatchFunctionPointer= */ (uintptr_t)&__guard_xfg_table_dispatch_icall_fptr, +#else + /* .GuardXFGCheckFunctionPointer = */ 0, + /* .GuardXFGDispatchFunctionPointer = */ 0, + /* .GuardXFGTableDispatchFunctionPointer= */ 0, +#endif + /* .CastGuardOsDeterminedFailureMode = */ 0, +}; + diff --git a/src/VBox/Runtime/common/compiler/vcc/purecall-vcc.cpp b/src/VBox/Runtime/common/compiler/vcc/purecall-vcc.cpp new file mode 100644 index 00000000..d76b3b4d --- /dev/null +++ b/src/VBox/Runtime/common/compiler/vcc/purecall-vcc.cpp @@ -0,0 +1,63 @@ +/* $Id: purecall-vcc.cpp $ */ +/** @file + * IPRT - No-CRT - Pure virtual method vtable filler. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "internal/nocrt.h" + +#include <iprt/asm.h> +#ifndef IPRT_NOCRT_WITHOUT_FATAL_WRITE +# include <iprt/assert.h> +#endif + +#include "internal/compiler-vcc.h" + + +extern "C" int __cdecl _purecall(void) +{ +#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE + RTAssertMsg2("\n\n!!%p called _purecall!!\n\n", ASMReturnAddress()); +#else + rtNoCrtFatalWriteBegin(RT_STR_TUPLE("\r\n\r\n!!_purecall called from ")); + rtNoCrtFatalWritePtr(ASMReturnAddress()); + rtNoCrtFatalWriteEnd(RT_STR_TUPLE("!!\r\n\r\n")); +#endif + RT_BREAKPOINT(); + return 0; +} + diff --git a/src/VBox/Runtime/common/compiler/vcc/stack-except-seh-vcc.cpp b/src/VBox/Runtime/common/compiler/vcc/stack-except-seh-vcc.cpp new file mode 100644 index 00000000..ff611e47 --- /dev/null +++ b/src/VBox/Runtime/common/compiler/vcc/stack-except-seh-vcc.cpp @@ -0,0 +1,117 @@ +/* $Id: stack-except-seh-vcc.cpp $ */ +/** @file + * IPRT - Visual C++ Compiler - Stack Checking, __GSHandlerCheck_SEH. + */ + +/* + * Copyright (C) 2022-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "internal/nocrt.h" + +#include "except-vcc.h" + + +#if !defined(RT_ARCH_AMD64) +# error "This file is for AMD64 (and probably ARM, but needs porting)" +#endif + + + +/** + * Check the stack cookie before calling the exception handler. + * + * This is to prevent attackers from bypassing stack cookie checking by + * triggering an exception. + * + * This is called for windows' structured exception handling (SEH), i.e. the + * __try/__except/__finally stuff in Visual C++, for which the compiler + * generates somewhat different strctures compared to the plain __GSHanderCheck + * scenario. + * + * @returns Exception disposition. + * @param pXcptRec The exception record. + * @param pXcptRegRec The exception registration record, taken to be the frame + * address. + * @param pCpuCtx The CPU context for the exception. + * @param pDispCtx Dispatcher context. + */ +extern "C" __declspec(guard(suppress)) +EXCEPTION_DISPOSITION __GSHandlerCheck_SEH(PEXCEPTION_RECORD pXcptRec, PEXCEPTION_REGISTRATION_RECORD pXcptRegRec, + PCONTEXT pCpuCtx, PDISPATCHER_CONTEXT pDispCtx) +{ + /* + * The HandlerData points to a scope table, which is then followed by GS_HANDLER_DATA. + * + * Sample offCookie values: 0521H (tst.cpp), 02caH (installNetLwf), and 0502H (installNetFlt). + */ + SCOPE_TABLE const *pScopeTable = (SCOPE_TABLE const *)pDispCtx->HandlerData; + PCGS_HANDLER_DATA pHandlerData = (PCGS_HANDLER_DATA)&pScopeTable->ScopeRecord[pScopeTable->Count]; + + /* + * Locate the stack cookie and call the regular stack cookie checker routine. + * (Same code as in __GSHandlerCheck, fixes applies both places.) + */ + /* Calculate the cookie address and read it. */ + uintptr_t uPtrFrame = (uintptr_t)pXcptRegRec; + uint32_t offCookie = pHandlerData->u.offCookie; + if (offCookie & GS_HANDLER_OFF_COOKIE_HAS_ALIGNMENT) + { + uPtrFrame += pHandlerData->offAlignedBase; + uPtrFrame &= ~(uintptr_t)pHandlerData->uAlignmentMask; + } + uintptr_t uCookie = *(uintptr_t const *)(uPtrFrame + (int32_t)(offCookie & GS_HANDLER_OFF_COOKIE_MASK)); + + /* The stored cookie is xor'ed with the frame / registration record address + or with the frame pointer register if one is being used. In the latter + case, we have to add the frame offset to get the correct address. */ + uintptr_t uXorAddr = (uintptr_t)pXcptRegRec; + PCIMAGE_UNWIND_INFO pUnwindInfo = (PCIMAGE_UNWIND_INFO)(pDispCtx->ImageBase + pDispCtx->FunctionEntry->UnwindInfoAddress); + if (pUnwindInfo->FrameRegister != 0) + uXorAddr += pUnwindInfo->FrameOffset << 4; + + /* This call will not return on failure. */ + __security_check_cookie(uCookie ^ uXorAddr); + + + /* + * Now call the handler if the GS handler data indicates that we ought to. + */ + if ( (IS_UNWINDING(pXcptRec->ExceptionFlags) ? GS_HANDLER_OFF_COOKIE_IS_UHANDLER : GS_HANDLER_OFF_COOKIE_IS_EHANDLER) + & pHandlerData->u.offCookie) + return __C_specific_handler(pXcptRec, pXcptRegRec, pCpuCtx, pDispCtx); + + return ExceptionContinueSearch; +} + diff --git a/src/VBox/Runtime/common/compiler/vcc/stack-except-vcc.cpp b/src/VBox/Runtime/common/compiler/vcc/stack-except-vcc.cpp new file mode 100644 index 00000000..13ea35b1 --- /dev/null +++ b/src/VBox/Runtime/common/compiler/vcc/stack-except-vcc.cpp @@ -0,0 +1,106 @@ +/* $Id: stack-except-vcc.cpp $ */ +/** @file + * IPRT - Visual C++ Compiler - Stack Checking, __GSHandlerCheck. + */ + +/* + * Copyright (C) 2022-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "internal/nocrt.h" + +#include "except-vcc.h" + + +#if !defined(RT_ARCH_AMD64) +# error "This file is for AMD64 (and probably ARM, but needs porting)" +#endif + + + +/** + * Check the stack cookie before calling the exception handler. + * + * This is to prevent attackers from bypassing stack cookie checking by + * triggering an exception. + * + * This does not call any C++ exception handlers, as it's probably (still + * figuring this stuff out) only used when C++ exceptions are disabled. + * + * @returns Exception disposition. + * @param pXcptRec The exception record. + * @param pXcptRegRec The exception registration record, taken to be the frame + * address. + * @param pCpuCtx The CPU context for the exception. + * @param pDispCtx Dispatcher context. + */ +extern "C" __declspec(guard(suppress)) +EXCEPTION_DISPOSITION __GSHandlerCheck(PEXCEPTION_RECORD pXcptRec, PEXCEPTION_REGISTRATION_RECORD pXcptRegRec, + PCONTEXT pCpuCtx, PDISPATCHER_CONTEXT pDispCtx) +{ + RT_NOREF(pXcptRec, pCpuCtx); + + /* + * Only GS handler data here. + */ + PCGS_HANDLER_DATA pHandlerData = (PCGS_HANDLER_DATA)pDispCtx->HandlerData; + + /* + * Locate the stack cookie and call the regular stack cookie checker routine. + * (Same code as in __GSHandlerCheck_SEH, fixes applies both places.) + */ + /* Calculate the cookie address and read it. */ + uintptr_t uPtrFrame = (uintptr_t)pXcptRegRec; + uint32_t offCookie = pHandlerData->u.offCookie; + if (offCookie & GS_HANDLER_OFF_COOKIE_HAS_ALIGNMENT) + { + uPtrFrame += pHandlerData->offAlignedBase; + uPtrFrame &= ~(uintptr_t)pHandlerData->uAlignmentMask; + } + uintptr_t uCookie = *(uintptr_t const *)(uPtrFrame + (int32_t)(offCookie & GS_HANDLER_OFF_COOKIE_MASK)); + + /* The stored cookie is xor'ed with the frame / registration record address + or with the frame pointer register if one is being used. In the latter + case, we have to add the frame offset to get the correct address. */ + uintptr_t uXorAddr = (uintptr_t)pXcptRegRec; + PCIMAGE_UNWIND_INFO pUnwindInfo = (PCIMAGE_UNWIND_INFO)(pDispCtx->ImageBase + pDispCtx->FunctionEntry->UnwindInfoAddress); + if (pUnwindInfo->FrameRegister != 0) + uXorAddr += pUnwindInfo->FrameOffset << 4; + + /* This call will not return on failure. */ + __security_check_cookie(uCookie ^ uXorAddr); + + return ExceptionContinueSearch; +} + diff --git a/src/VBox/Runtime/common/compiler/vcc/stack-probe-vcc.asm b/src/VBox/Runtime/common/compiler/vcc/stack-probe-vcc.asm new file mode 100644 index 00000000..da2e2965 --- /dev/null +++ b/src/VBox/Runtime/common/compiler/vcc/stack-probe-vcc.asm @@ -0,0 +1,157 @@ +; $Id: stack-probe-vcc.asm $ +;; @file +; IPRT - Stack related Visual C++ support routines. +; + +; +; Copyright (C) 2022-2023 Oracle and/or its affiliates. +; +; This file is part of VirtualBox base platform packages, as +; available from https://www.virtualbox.org. +; +; This program is free software; you can redistribute it and/or +; modify it under the terms of the GNU General Public License +; as published by the Free Software Foundation, in version 3 of the +; License. +; +; This program is distributed in the hope that it will be useful, but +; WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; if not, see <https://www.gnu.org/licenses>. +; +; The contents of this file may alternatively be used under the terms +; of the Common Development and Distribution License Version 1.0 +; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +; in the VirtualBox distribution, in which case the provisions of the +; CDDL are applicable instead of those of the GPL. +; +; You may elect to license modified versions of this file under the +; terms and conditions of either the GPL or the CDDL or both. +; +; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +; + + + +;********************************************************************************************************************************* +;* Header Files * +;********************************************************************************************************************************* +%if 0 ; YASM's builtin SEH64 support doesn't cope well with code alignment, so use our own. + %define RT_ASM_WITH_SEH64 +%else + %define RT_ASM_WITH_SEH64_ALT +%endif +%include "iprt/asmdefs.mac" +%include "iprt/x86.mac" + + +;; +; Probe stack to trigger guard faults, and for x86 to allocate stack space. +; +; @param xAX Frame size. +; @uses AMD64: Probably nothing. EAX is certainly not supposed to change. +; x86: ESP = ESP - EAX; EFLAGS, nothing else +; +ALIGNCODE(64) +GLOBALNAME_RAW __alloca_probe, __alloca_probe, function +BEGINPROC_RAW __chkstk + push xBP + SEH64_PUSH_xBP + mov xBP, xSP + SEH64_SET_FRAME_xBP 0 + push xAX + SEH64_PUSH_GREG xAX + push xBX + SEH64_PUSH_GREG xBX + SEH64_END_PROLOGUE + + ; + ; Adjust eax so we're relative to [xBP - xCB*2]. + ; + sub xAX, xCB * 4 + jle .touch_loop_done ; jump if rax < xCB*4, very unlikely + + ; + ; Subtract what's left of the current page from eax and only engage + ; the touch loop if (int)xAX > 0. + ; + lea ebx, [ebp - xCB * 2] + and ebx, PAGE_SIZE - 1 + sub xAX, xBX + jnl .touch_loop ; jump if pages to touch. + +.touch_loop_done: + pop xBX + pop xAX + leave +%ifndef RT_ARCH_X86 + ret +%else + ; + ; Do the stack space allocation and jump to the return location. + ; + sub esp, eax + add esp, 4 + jmp dword [esp + eax - 4] +%endif + + ; + ; The touch loop. + ; +.touch_loop: + sub xBX, PAGE_SIZE +%if 1 + mov [xBP + xBX - xCB * 2], bl +%else + or byte [xBP + xBX - xCB * 2], 0 ; non-destructive variant... +%endif + sub xAX, PAGE_SIZE + jnl .touch_loop + jmp .touch_loop_done +ENDPROC_RAW __chkstk + + +%ifdef RT_ARCH_X86 +;; +; 8 and 16 byte aligned alloca w/ probing. +; +; This routine adjusts the allocation size so __chkstk will return a +; correctly aligned allocation. +; +; @param xAX Unaligned allocation size. +; +%macro __alloc_probe_xxx 1 +ALIGNCODE(16) +BEGINPROC_RAW __alloca_probe_ %+ %1 + push ecx + + ; + ; Calc the ESP address after the allocation and adjust EAX so that it + ; will be aligned as desired. + ; + lea ecx, [esp + 8] + sub ecx, eax + and ecx, %1 - 1 + add eax, ecx + jc .bad_alloc_size +.continue: + + pop ecx + jmp __alloca_probe + +.bad_alloc_size: + %ifdef RT_STRICT + int3 + %endif + or eax, 0xfffffff0 + jmp .continue +ENDPROC_RAW __alloca_probe_ %+ %1 +%endmacro + +__alloc_probe_xxx 16 +__alloc_probe_xxx 8 +%endif ; RT_ARCH_X86 + diff --git a/src/VBox/Runtime/common/compiler/vcc/stack-vcc.asm b/src/VBox/Runtime/common/compiler/vcc/stack-vcc.asm new file mode 100644 index 00000000..edfd5750 --- /dev/null +++ b/src/VBox/Runtime/common/compiler/vcc/stack-vcc.asm @@ -0,0 +1,639 @@ +; $Id: stack-vcc.asm $ +;; @file +; IPRT - Stack related Visual C++ support routines. +; + +; +; Copyright (C) 2022-2023 Oracle and/or its affiliates. +; +; This file is part of VirtualBox base platform packages, as +; available from https://www.virtualbox.org. +; +; This program is free software; you can redistribute it and/or +; modify it under the terms of the GNU General Public License +; as published by the Free Software Foundation, in version 3 of the +; License. +; +; This program is distributed in the hope that it will be useful, but +; WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; if not, see <https://www.gnu.org/licenses>. +; +; The contents of this file may alternatively be used under the terms +; of the Common Development and Distribution License Version 1.0 +; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +; in the VirtualBox distribution, in which case the provisions of the +; CDDL are applicable instead of those of the GPL. +; +; You may elect to license modified versions of this file under the +; terms and conditions of either the GPL or the CDDL or both. +; +; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +; + + + +;********************************************************************************************************************************* +;* Header Files * +;********************************************************************************************************************************* +%if 0 ; YASM's builtin SEH64 support doesn't cope well with code alignment, so use our own. + %define RT_ASM_WITH_SEH64 +%else + %define RT_ASM_WITH_SEH64_ALT +%endif +%include "iprt/asmdefs.mac" +%include "iprt/x86.mac" +%ifdef RT_ARCH_AMD64 + %include "iprt/win/context-amd64.mac" +%else + %include "iprt/win/context-x86.mac" +%endif + + +;********************************************************************************************************************************* +;* Structures and Typedefs * +;********************************************************************************************************************************* + +;; Variable descriptor. +struc RTC_VAR_DESC_T + .offFrame resd 1 + .cbVar resd 1 + alignb RTCCPTR_CB + .pszName RTCCPTR_RES 1 +endstruc + +;; Frame descriptor. +struc RTC_FRAME_DESC_T + .cVars resd 1 + alignb RTCCPTR_CB + .paVars RTCCPTR_RES 1 ; Array of RTC_VAR_DESC_T. +endstruc + +;; An alloca allocation. +struc RTC_ALLOCA_ENTRY_T + .uGuard1 resd 1 + .pNext RTCCPTR_RES 1 ; Misaligned. +%if ARCH_BITS == 32 + .pNextPad resd 1 +%endif + .cb RTCCPTR_RES 1 ; Misaligned. +%if ARCH_BITS == 32 + .cbPad resd 1 +%endif + .auGuard2 resd 3 +endstruc + +%ifdef RT_ARCH_X86 + %define FASTCALL_NAME(a_Name, a_cbArgs) $@ %+ a_Name %+ @ %+ a_cbArgs +%else + %define FASTCALL_NAME(a_Name, a_cbArgs) NAME(a_Name) +%endif + + +;********************************************************************************************************************************* +;* Defined Constants And Macros * +;********************************************************************************************************************************* +%define VARIABLE_MARKER_PRE 0xcccccccc +%define VARIABLE_MARKER_POST 0xcccccccc + +%define ALLOCA_FILLER_BYTE 0xcc +%define ALLOCA_FILLER_32 0xcccccccc + + +;********************************************************************************************************************************* +;* Global Variables * +;********************************************************************************************************************************* +BEGINDATA +GLOBALNAME __security_cookie + dd 0xdeadbeef + dd 0x0c00ffe0 + + +;********************************************************************************************************************************* +;* External Symbols * +;********************************************************************************************************************************* +BEGINCODE +extern NAME(rtVccStackVarCorrupted) +extern NAME(rtVccSecurityCookieMismatch) +extern NAME(rtVccRangeCheckFailed) +%ifdef RT_ARCH_X86 +extern NAME(rtVccCheckEspFailed) +%endif + + + +;; +; This just initializes a global and calls _RTC_SetErrorFuncW to NULL, and +; since we don't have either of those we have nothing to do here. +BEGINPROC _RTC_InitBase + SEH64_END_PROLOGUE + ret +ENDPROC _RTC_InitBase + + +;; +; Nothing to do here. +BEGINPROC _RTC_Shutdown + SEH64_END_PROLOGUE + ret +ENDPROC _RTC_Shutdown + + + + +;; +; Checks stack variable markers. +; +; This seems to be a regular C function in the CRT, but x86 is conveniently +; using the fastcall convention which makes it very similar to amd64. +; +; We try make this as sleek as possible, leaving all the trouble for when we +; find a corrupted stack variable and need to call a C function to complain. +; +; @param pStackFrame The caller RSP/ESP. [RCX/ECX] +; @param pFrameDesc Frame descriptor. [RDX/EDX] +; +ALIGNCODE(64) +BEGINPROC_RAW FASTCALL_NAME(_RTC_CheckStackVars, 8) + push xBP + SEH64_PUSH_xBP + SEH64_END_PROLOGUE + + ; + ; Load the variable count into eax and check that it's not zero. + ; + mov eax, [xDX + RTC_FRAME_DESC_T.cVars] + test eax, eax + jz .return + + ; + ; Make edx/rdx point to the current variable and xBP be the frame pointer. + ; The latter frees up xCX for scratch use and incidentally make stack access + ; go via SS instead of DS (mostly irrlevant in 64-bit and 32-bit mode). + ; + mov xDX, [xDX + RTC_FRAME_DESC_T.paVars] + mov xBP, xCX + + ; + ; Loop thru the variables and check that their markers/fences haven't be + ; trampled over. + ; +.next_var: + ; Marker before the variable. +%if ARCH_BITS == 64 + movsxd rcx, dword [xDX + RTC_VAR_DESC_T.offFrame] +%else + mov xCX, dword [xDX + RTC_VAR_DESC_T.offFrame] +%endif + cmp dword [xBP + xCX - 4], VARIABLE_MARKER_PRE + jne rtVccCheckStackVarsFailed + + ; Marker after the variable. + add ecx, dword [xDX + RTC_VAR_DESC_T.cbVar] +%if ARCH_BITS == 64 + movsxd rcx, ecx +%endif + cmp dword [xBP + xCX], VARIABLE_MARKER_POST + jne rtVccCheckStackVarsFailed + + ; + ; Advance to the next variable. + ; +.advance: + add xDX, RTC_VAR_DESC_T_size + dec eax + jnz .next_var + + ; + ; Return. + ; +.return: + pop xBP + ret +ENDPROC_RAW FASTCALL_NAME(_RTC_CheckStackVars, 8) + +; +; Sub-function for _RTC_CheckStackVars, for purposes of SEH64 unwinding. +; +; Note! While we consider this fatal and will terminate the application, the +; compiler guys do not seem to think it is all that horrible and will +; report failure, maybe do an int3, and then try continue execution. +; +BEGINPROC_RAW rtVccCheckStackVarsFailed + nop ;push xBP - done in parent function + SEH64_PUSH_xBP + mov xCX, xBP ; xCX = caller pStackFrame. xBP free to become frame pointer. + mov xBP, xSP + SEH64_SET_FRAME_xBP 0 + pushf + push xAX + SEH64_PUSH_GREG xAX + sub xSP, CONTEXT_SIZE + 20h + SEH64_ALLOCATE_STACK (CONTEXT_SIZE + 20h) + SEH64_END_PROLOGUE + + lea xAX, [xBP - CONTEXT_SIZE] + call NAME(rtVccCaptureContext) + + ; rtVccStackVarCorrupted(uint8_t *pbFrame, RTC_VAR_DESC_T const *pVar, PCONTEXT) +.again: +%ifdef RT_ARCH_AMD64 + lea r8, [xBP - CONTEXT_SIZE] +%else + lea xAX, [xBP - CONTEXT_SIZE] + mov [xSP + 8], xAX + mov [xSP + 4], xDX + mov [xSP], xCX +%endif + call NAME(rtVccStackVarCorrupted) + jmp .again +ENDPROC_RAW rtVccCheckStackVarsFailed + + +%ifdef RT_ARCH_X86 +;; +; Called to follow up on a 'CMP ESP, EBP' kind of instruction, +; expected to report failure if the compare failed. +; +; Note! While we consider this fatal and will terminate the application, the +; compiler guys do not seem to think it is all that horrible and will +; report failure, maybe do an int3, and then try continue execution. +; +ALIGNCODE(16) +BEGINPROC _RTC_CheckEsp + jne .unexpected_esp + ret + +.unexpected_esp: + push xBP + SEH64_PUSH_xBP + mov xBP, xSP + SEH64_SET_FRAME_xBP 0 + pushf + push xAX + SEH64_PUSH_GREG xAX + sub xSP, CONTEXT_SIZE + 20h + SEH64_ALLOCATE_STACK (CONTEXT_SIZE + 20h) + SEH64_END_PROLOGUE + + lea xAX, [xBP - CONTEXT_SIZE] + call NAME(rtVccCaptureContext) + + ; rtVccCheckEspFailed(PCONTEXT) +.again: + lea xAX, [xBP - CONTEXT_SIZE] +%ifdef RT_ARCH_AMD64 + mov xCX, xAX +%else + mov [xSP], xAX +%endif + call NAME(rtVccCheckEspFailed) + jmp .again + +ENDPROC _RTC_CheckEsp +%endif ; RT_ARCH_X86 + + + +;; +; Initialize an alloca allocation list entry and add it to it. +; +; When this is call, presumably _RTC_CheckStackVars2 is used to verify the frame. +; +; @param pNewEntry Pointer to the new entry. [RCX/ECX] +; @param cbEntry The entry size, including header. [RDX/EDX] +; @param ppHead Pointer to the list head pointer. [R8/stack] +; +ALIGNCODE(64) +BEGINPROC_RAW FASTCALL_NAME(_RTC_AllocaHelper, 12) + SEH64_END_PROLOGUE + + ; + ; Check that input isn't NULL or the size isn't zero. + ; + test xCX, xCX + jz .return + test xDX, xDX + jz .return +%if ARCH_BITS == 64 + test r8, r8 +%else + cmp dword [xSP + xCB], 0 +%endif + jz .return + + ; + ; Memset the memory to ALLOCA_FILLER + ; +%if ARCH_BITS == 64 + mov r10, rdi ; save rdi + mov r11, rcx ; save pNewEntry +%else + push xDI + push xCX + cld ; paranoia +%endif + + mov al, ALLOCA_FILLER_BYTE + mov xDI, xCX ; entry pointer + mov xCX, xDX ; entry size (in bytes) + rep stosb + +%if ARCH_BITS == 64 + mov rdi, r10 +%else + pop xCX + pop xDI +%endif + + ; + ; Fill in the entry and link it as onto the head of the chain. + ; +%if ARCH_BITS == 64 + mov [r11 + RTC_ALLOCA_ENTRY_T.cb], xDX + mov xAX, [r8] + mov [r11 + RTC_ALLOCA_ENTRY_T.pNext], xAX + mov [r8], r11 +%else + mov [xCX + RTC_ALLOCA_ENTRY_T.cb], xDX + mov xAX, [xSP + xCB] ; ppHead + mov xDX, [xAX] + mov [xCX + RTC_ALLOCA_ENTRY_T.pNext], xDX + mov [xAX], xCX +%endif + +.return: +%if ARCH_BITS == 64 + ret +%else + ret 4 +%endif +ENDPROC_RAW FASTCALL_NAME(_RTC_AllocaHelper, 12) + + +;; +; Checks if the security cookie ok, complaining and terminating if it isn't. +; +ALIGNCODE(16) +BEGINPROC_RAW FASTCALL_NAME(__security_check_cookie, 4) + SEH64_END_PROLOGUE + cmp xCX, [NAME(__security_cookie) xWrtRIP] + jne rtVccSecurityCookieFailed + ;; amd64 version checks if the top 16 bits are zero, we skip that for now. + ret +ENDPROC_RAW FASTCALL_NAME(__security_check_cookie, 4) + +; Sub-function for __security_check_cookie, for purposes of SEH64 unwinding. +BEGINPROC_RAW rtVccSecurityCookieFailed + push xBP + SEH64_PUSH_xBP + mov xBP, xSP + SEH64_SET_FRAME_xBP 0 + pushf + push xAX + SEH64_PUSH_GREG xAX + sub xSP, CONTEXT_SIZE + 20h + SEH64_ALLOCATE_STACK (CONTEXT_SIZE + 20h) + SEH64_END_PROLOGUE + + lea xAX, [xBP - CONTEXT_SIZE] + call NAME(rtVccCaptureContext) + + ; rtVccSecurityCookieMismatch(uCookie, PCONTEXT) +.again: +%ifdef RT_ARCH_AMD64 + lea xDX, [xBP - CONTEXT_SIZE] +%else + lea xAX, [xBP - CONTEXT_SIZE] + mov [xSP + 4], xAX + mov [xSP], xCX +%endif + call NAME(rtVccSecurityCookieMismatch) + jmp .again +ENDPROC_RAW rtVccSecurityCookieFailed + + +;; +; Generated when using /GS - buffer security checks - so, fatal. +; +; Doesn't seem to take any parameters. +; +BEGINPROC __report_rangecheckfailure + push xBP + SEH64_PUSH_xBP + mov xBP, xSP + SEH64_SET_FRAME_xBP 0 + pushf + push xAX + SEH64_PUSH_GREG xAX + sub xSP, CONTEXT_SIZE + 20h + SEH64_ALLOCATE_STACK (CONTEXT_SIZE + 20h) + SEH64_END_PROLOGUE + + lea xAX, [xBP - CONTEXT_SIZE] + call NAME(rtVccCaptureContext) + + ; rtVccRangeCheckFailed(PCONTEXT) +.again: + lea xAX, [xBP - CONTEXT_SIZE] +%ifdef RT_ARCH_AMD64 + mov xCX, xAX +%else + mov [xSP], xAX +%endif + call NAME(rtVccRangeCheckFailed) + jmp .again +ENDPROC __report_rangecheckfailure + + +%if 0 ; Currently not treating these as completely fatal, just like the + ; compiler guys do. I'm sure the compiler only generate these calls + ; if it thinks a variable could be used uninitialized, however I'm not + ; really sure if there is a runtime check in addition or if it's an + ; action that always will be taken in a code path deemed to be bad. + ; Judging from the warnings, the compile time analysis leave lots to be + ; desired (lots of false positives). +;; +; Not entirely sure how and when the compiler generates these. +; extern "C" void __cdecl _RTC_UninitUse(const char *pszVar) +BEGINPROC _RTC_UninitUse + push xBP + SEH64_PUSH_xBP + mov xBP, xSP + SEH64_SET_FRAME_xBP 0 + pushf + push xAX + SEH64_PUSH_GREG xAX + sub xSP, CONTEXT_SIZE + 20h + SEH64_ALLOCATE_STACK (CONTEXT_SIZE + 20h) + SEH64_END_PROLOGUE + + lea xAX, [xBP - CONTEXT_SIZE] + call NAME(rtVccCaptureContext) + + extern NAME(rtVccUninitializedVariableUse) + ; rtVccUninitializedVariableUse(const char *pszVar, PCONTEXT) +.again: +%ifdef RT_ARCH_AMD64 + lea xDX, [xBP - CONTEXT_SIZE] +%else + lea xAX, [xBP - CONTEXT_SIZE] + mov [xSP + xCB], xAX + mov xAX, [xBP + xCB * 2] + mov [xSP], xAX +%endif + call NAME(rtVccUninitializedVariableUse) + jmp .again +ENDPROC _RTC_UninitUse +%endif + +;; +; Internal worker that creates a CONTEXT record for the caller. +; +; This expects a old-style stack frame setup, with xBP as base, such that: +; xBP+xCB*1: Return address -> Rip/Eip +; xBP+xCB*0: Return xBP -> Rbp/Ebp +; xBP-xCB*1: EFLAGS -> EFlags +; xBP-xCB*2: Saved xAX -> Rax/Eax +; +; @param pCtx xAX Pointer to a CONTEXT structure. +; +BEGINPROC rtVccCaptureContext + SEH64_END_PROLOGUE + +%ifdef RT_ARCH_AMD64 + mov [xAX + CONTEXT.Rcx], rcx + mov [xAX + CONTEXT.Rdx], rdx + mov rcx, [xBP - xCB*2] + mov [xAX + CONTEXT.Rax], ecx + mov [xAX + CONTEXT.Rbx], rbx + lea rcx, [xBP + xCB*2] + mov [xAX + CONTEXT.Rsp], rcx + mov rdx, [xBP] + mov [xAX + CONTEXT.Rbp], rdx + mov [xAX + CONTEXT.Rdi], rdi + mov [xAX + CONTEXT.Rsi], rsi + mov [xAX + CONTEXT.R8], r8 + mov [xAX + CONTEXT.R9], r9 + mov [xAX + CONTEXT.R10], r10 + mov [xAX + CONTEXT.R11], r11 + mov [xAX + CONTEXT.R12], r12 + mov [xAX + CONTEXT.R13], r13 + mov [xAX + CONTEXT.R14], r14 + mov [xAX + CONTEXT.R15], r15 + + mov rcx, [xBP + xCB*1] + mov [xAX + CONTEXT.Rip], rcx + mov edx, [xBP - xCB*1] + mov [xAX + CONTEXT.EFlags], edx + + mov dx, ss + mov [xAX + CONTEXT.SegSs], dx + mov cx, cs + mov [xAX + CONTEXT.SegCs], cx + mov dx, ds + mov [xAX + CONTEXT.SegDs], dx + mov cx, es + mov [xAX + CONTEXT.SegEs], cx + mov dx, fs + mov [xAX + CONTEXT.SegFs], dx + mov cx, gs + mov [xAX + CONTEXT.SegGs], cx + + mov dword [xAX + CONTEXT.ContextFlags], CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS + + ; Clear stuff we didn't set. + xor edx, edx + mov [xAX + CONTEXT.P1Home], rdx + mov [xAX + CONTEXT.P2Home], rdx + mov [xAX + CONTEXT.P3Home], rdx + mov [xAX + CONTEXT.P4Home], rdx + mov [xAX + CONTEXT.P5Home], rdx + mov [xAX + CONTEXT.P6Home], rdx + mov [xAX + CONTEXT.MxCsr], edx + mov [xAX + CONTEXT.Dr0], rdx + mov [xAX + CONTEXT.Dr1], rdx + mov [xAX + CONTEXT.Dr2], rdx + mov [xAX + CONTEXT.Dr3], rdx + mov [xAX + CONTEXT.Dr6], rdx + mov [xAX + CONTEXT.Dr7], rdx + + mov ecx, CONTEXT_size - CONTEXT.FltSave + AssertCompile(((CONTEXT_size - CONTEXT.FltSave) % 8) == 0) +.again: + mov [xAX + CONTEXT.FltSave + xCX - 8], rdx + sub ecx, 8 + jnz .again + + ; Restore edx and ecx. + mov rcx, [xAX + CONTEXT.Rcx] + mov rdx, [xAX + CONTEXT.Rdx] + +%elifdef RT_ARCH_X86 + + mov [xAX + CONTEXT.Ecx], ecx + mov [xAX + CONTEXT.Edx], edx + mov ecx, [xBP - xCB*2] + mov [xAX + CONTEXT.Eax], ecx + mov [xAX + CONTEXT.Ebx], ebx + lea ecx, [xBP + xCB*2] + mov [xAX + CONTEXT.Esp], ecx + mov edx, [xBP] + mov [xAX + CONTEXT.Ebp], edx + mov [xAX + CONTEXT.Edi], edi + mov [xAX + CONTEXT.Esi], esi + + mov ecx, [xBP + xCB] + mov [xAX + CONTEXT.Eip], ecx + mov ecx, [xBP - xCB*1] + mov [xAX + CONTEXT.EFlags], ecx + + mov dx, ss + movzx edx, dx + mov [xAX + CONTEXT.SegSs], edx + mov cx, cs + movzx ecx, cx + mov [xAX + CONTEXT.SegCs], ecx + mov dx, ds + movzx edx, dx + mov [xAX + CONTEXT.SegDs], edx + mov cx, es + movzx ecx, cx + mov [xAX + CONTEXT.SegEs], ecx + mov dx, fs + movzx edx, dx + mov [xAX + CONTEXT.SegFs], edx + mov cx, gs + movzx ecx, cx + mov [xAX + CONTEXT.SegGs], ecx + + mov dword [xAX + CONTEXT.ContextFlags], CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS + + ; Clear stuff we didn't set. + xor edx, edx + mov [xAX + CONTEXT.Dr0], edx + mov [xAX + CONTEXT.Dr1], edx + mov [xAX + CONTEXT.Dr2], edx + mov [xAX + CONTEXT.Dr3], edx + mov [xAX + CONTEXT.Dr6], edx + mov [xAX + CONTEXT.Dr7], edx + + mov ecx, CONTEXT_size - CONTEXT.ExtendedRegisters +.again: + mov [xAX + CONTEXT.ExtendedRegisters + xCX - 4], edx + sub ecx, 4 + jnz .again + + ; Restore edx and ecx. + mov ecx, [xAX + CONTEXT.Ecx] + mov edx, [xAX + CONTEXT.Edx] + +%else + %error RT_ARCH +%endif + ret +ENDPROC rtVccCaptureContext + diff --git a/src/VBox/Runtime/common/compiler/vcc/stacksup-vcc.cpp b/src/VBox/Runtime/common/compiler/vcc/stacksup-vcc.cpp new file mode 100644 index 00000000..8794cd07 --- /dev/null +++ b/src/VBox/Runtime/common/compiler/vcc/stacksup-vcc.cpp @@ -0,0 +1,371 @@ +/* $Id: stacksup-vcc.cpp $ */ +/** @file + * IPRT - Visual C++ Compiler - Stack Checking C/C++ Support. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "internal/nocrt.h" + +#include <iprt/asm.h> +#include <iprt/asm-amd64-x86.h> +#ifndef IPRT_NOCRT_WITHOUT_FATAL_WRITE +# include <iprt/assert.h> +#endif + +#include "internal/compiler-vcc.h" +#ifdef IN_RING3 +# include <iprt/win/windows.h> +# include "../../../r3/win/internal-r3-win.h" /* ugly, but need some windows API function pointers */ +#endif + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** Gets the program counter member of Windows' CONTEXT structure. */ +#if defined(RT_ARCH_AMD64) +# define MY_GET_PC_FROM_CONTEXT(a_pCtx) ((a_pCtx)->Rip) +#elif defined(RT_ARCH_X86) +# define MY_GET_PC_FROM_CONTEXT(a_pCtx) ((a_pCtx)->Eip) +#else +# error "Port Me!" +#endif + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** Variable descriptor. */ +typedef struct RTC_VAR_DESC_T +{ + int32_t offFrame; + uint32_t cbVar; + const char *pszName; +} RTC_VAR_DESC_T; + +/** Frame descriptor. */ +typedef struct RTC_FRAME_DESC_T +{ + uint32_t cVars; + RTC_VAR_DESC_T const *paVars; +} RTC_FRAME_DESC_T; + +#define VARIABLE_MARKER_PRE 0xcccccccc +#define VARIABLE_MARKER_POST 0xcccccccc + + +/** + * Alloca allocation entry. + * @note For whatever reason the pNext and cb members are misaligned on 64-bit + * targets. 32-bit targets OTOH adds padding to keep the structure size + * and pNext + cb offsets the same. + */ +#pragma pack(4) +typedef struct RTC_ALLOC_ENTRY +{ + uint32_t uGuard1; + RTC_ALLOC_ENTRY *pNext; +#if ARCH_BITS == 32 + uint32_t pNextPad; +#endif + size_t cb; +#if ARCH_BITS == 32 + uint32_t cbPad; +#endif + uint32_t auGuard2[3]; +} RTC_ALLOC_ENTRY; +#pragma pack() + +#define ALLOCA_FILLER_BYTE 0xcc +#define ALLOCA_FILLER_32 0xcccccccc + + +/********************************************************************************************************************************* +* External Symbols * +*********************************************************************************************************************************/ +extern "C" void __fastcall _RTC_CheckStackVars(uint8_t *pbFrame, RTC_VAR_DESC_T const *pVar); /* nocrt-stack.asm */ +extern "C" uintptr_t __security_cookie; + + +/** + * Initializes the security cookie value. + * + * This must be called as the first thing by the startup code. We must also no + * do anything fancy here. + */ +void rtVccInitSecurityCookie(void) RT_NOEXCEPT +{ + __security_cookie = (uintptr_t)ASMReadTSC() ^ (uintptr_t)&__security_cookie; +} + + +/** + * Reports a security error. + * + * @param uFastFailCode The fast fail code. + * @param pCpuCtx The CPU context at the failure location. + */ +static DECL_NO_RETURN(void) rtVccFatalSecurityErrorWithCtx(uint32_t uFastFailCode, PCONTEXT pCpuCtx) +{ +#ifdef IN_RING3 + /* + * Use the __fastfail() approach if available, it is more secure than the stuff below: + */ + if (g_pfnIsProcessorFeaturePresent && g_pfnIsProcessorFeaturePresent(PF_FASTFAIL_AVAILABLE)) + __fastfail(uFastFailCode); + + /* + * Fallback for legacy systems. + */ + if (g_pfnIsDebuggerPresent && g_pfnIsDebuggerPresent()) + __debugbreak(); + + /* If we can, clear the unhandled exception filter and report and unhandled exception. */ + if (g_pfnSetUnhandledExceptionFilter && g_pfnUnhandledExceptionFilter) + { + g_pfnSetUnhandledExceptionFilter(NULL); + + EXCEPTION_RECORD XcptRec = + { + /* .ExceptionCode = */ STATUS_STACK_BUFFER_OVERRUN, + /* .ExceptionFlags = */ EXCEPTION_NONCONTINUABLE, + /* .ExceptionRecord = */ NULL, +# ifdef RT_ARCH_AMD64 + /* .ExceptionAddress = */ (void *)pCpuCtx->Rip, +# elif defined(RT_ARCH_X86) + /* .ExceptionAddress = */ (void *)pCpuCtx->Eip, +# else +# error "Port me!" +# endif + /* .NumberParameters = */ 1, + /* .ExceptionInformation = */ { uFastFailCode, } + }; + + EXCEPTION_POINTERS XcptPtrs = { &XcptRec, pCpuCtx }; + g_pfnUnhandledExceptionFilter(&XcptPtrs); + } + + for (;;) + TerminateProcess(GetCurrentProcess(), STATUS_STACK_BUFFER_OVERRUN); + +#else +# error "Port ME!" +#endif +} + + +DECLASM(void) rtVccStackVarCorrupted(uint8_t *pbFrame, RTC_VAR_DESC_T const *pVar, PCONTEXT pCpuCtx) +{ +#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE + RTAssertMsg2("\n\n!!Stack corruption!!\n\n" + "%p LB %#x - %s\n", + pbFrame + pVar->offFrame, pVar->cbVar, pVar->pszName); +#else + rtNoCrtFatalWriteBegin(RT_STR_TUPLE("\r\n\r\n!!Stack corruption!!\r\n\r\n")); + rtNoCrtFatalWritePtr(pbFrame + pVar->offFrame); + rtNoCrtFatalWrite(RT_STR_TUPLE(" LB ")); + rtNoCrtFatalWriteX32(pVar->cbVar); + rtNoCrtFatalWrite(RT_STR_TUPLE(" - ")); + rtNoCrtFatalWriteStr(pVar->pszName); + rtNoCrtFatalWriteEnd(RT_STR_TUPLE("\r\n")); +#endif + rtVccFatalSecurityErrorWithCtx(FAST_FAIL_INCORRECT_STACK, pCpuCtx); +} + + +DECLASM(void) rtVccSecurityCookieMismatch(uintptr_t uCookie, PCONTEXT pCpuCtx) +{ +#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE + RTAssertMsg2("\n\n!!Stack cookie corruption!!\n\n" + "expected %p, found %p\n", + __security_cookie, uCookie); +#else + rtNoCrtFatalWriteBegin(RT_STR_TUPLE("\r\n\r\n!!Stack cookie corruption!!\r\n\r\n" + "expected")); + rtNoCrtFatalWritePtr((void *)__security_cookie); + rtNoCrtFatalWrite(RT_STR_TUPLE(", found ")); + rtNoCrtFatalWritePtr((void *)uCookie); + rtNoCrtFatalWriteEnd(RT_STR_TUPLE("\r\n")); +#endif + rtVccFatalSecurityErrorWithCtx(FAST_FAIL_STACK_COOKIE_CHECK_FAILURE, pCpuCtx); +} + + +#ifdef RT_ARCH_X86 +DECLASM(void) rtVccCheckEspFailed(PCONTEXT pCpuCtx) +{ +# ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE + RTAssertMsg2("\n\n!!ESP check failed!!\n\n" + "eip=%p esp=%p ebp=%p\n", + pCpuCtx->Eip, pCpuCtx->Esp, pCpuCtx->Ebp); +# else + rtNoCrtFatalWriteBegin(RT_STR_TUPLE("\r\n\r\n!!ESP check failed!!\r\n\r\n" + "eip=")); + rtNoCrtFatalWritePtr((void *)pCpuCtx->Eip); + rtNoCrtFatalWrite(RT_STR_TUPLE(" esp=")); + rtNoCrtFatalWritePtr((void *)pCpuCtx->Esp); + rtNoCrtFatalWrite(RT_STR_TUPLE(" ebp=")); + rtNoCrtFatalWritePtr((void *)pCpuCtx->Ebp); + rtNoCrtFatalWriteEnd(RT_STR_TUPLE("\r\n")); +# endif + rtVccFatalSecurityErrorWithCtx(FAST_FAIL_INCORRECT_STACK, pCpuCtx); +} +#endif + + +/** @todo reimplement in assembly (feeling too lazy right now). */ +extern "C" void __fastcall _RTC_CheckStackVars2(uint8_t *pbFrame, RTC_VAR_DESC_T const *pVar, RTC_ALLOC_ENTRY *pHead) +{ + while (pHead) + { + if ( pHead->uGuard1 == ALLOCA_FILLER_32 +#if 1 && ARCH_BITS == 32 + && pHead->pNextPad == ALLOCA_FILLER_32 + && pHead->cbPad == ALLOCA_FILLER_32 +#endif + && pHead->auGuard2[0] == ALLOCA_FILLER_32 + && pHead->auGuard2[1] == ALLOCA_FILLER_32 + && pHead->auGuard2[2] == ALLOCA_FILLER_32 + && *(uint32_t const *)((uint8_t const *)pHead + pHead->cb - sizeof(uint32_t)) == ALLOCA_FILLER_32) + { /* likely */ } + else + { +#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE + RTAssertMsg2("\n\n!!Stack corruption (alloca)!!\n\n" + "%p LB %#x\n", + pHead, pHead->cb); +#else + rtNoCrtFatalWriteBegin(RT_STR_TUPLE("\r\n\r\n!!Stack corruption (alloca)!!\r\n\r\n")); + rtNoCrtFatalWritePtr(pHead); + rtNoCrtFatalWrite(RT_STR_TUPLE(" LB ")); + rtNoCrtFatalWriteX64(pHead->cb); + rtNoCrtFatalWriteEnd(RT_STR_TUPLE("\r\n")); +#endif +#ifdef IN_RING3 + if (g_pfnIsDebuggerPresent && g_pfnIsDebuggerPresent()) +#endif + RT_BREAKPOINT(); + } + pHead = pHead->pNext; + } + + _RTC_CheckStackVars(pbFrame, pVar); +} + + +DECLASM(void) rtVccRangeCheckFailed(PCONTEXT pCpuCtx) +{ +# ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE + RTAssertMsg2("\n\n!!Range check failed at %p!!\n\n", MY_GET_PC_FROM_CONTEXT(pCpuCtx)); +# else + rtNoCrtFatalWriteBegin(RT_STR_TUPLE("\r\n\r\n!!Range check failed at ")); + rtNoCrtFatalWritePtr((void *)MY_GET_PC_FROM_CONTEXT(pCpuCtx)); + rtNoCrtFatalWriteEnd(RT_STR_TUPLE("!!\r\n")); +# endif + rtVccFatalSecurityErrorWithCtx(FAST_FAIL_RANGE_CHECK_FAILURE, pCpuCtx); +} + + +/** Whether or not this should be a fatal issue remains to be seen. See + * explanation in stack-vcc.asm. */ +#if 0 +DECLASM(void) rtVccUninitializedVariableUse(const char *pszVar, PCONTEXT pCpuCtx) +#else +extern "C" void __cdecl _RTC_UninitUse(const char *pszVar) +#endif +{ +#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE + RTAssertMsg2("\n\n!!Used uninitialized variable %s at %p!!\n\n", + pszVar ? pszVar : "", ASMReturnAddress()); +#else + rtNoCrtFatalWriteBegin(RT_STR_TUPLE("\r\n\r\n!!Used uninitialized variable ")); + rtNoCrtFatalWriteStr(pszVar); + rtNoCrtFatalWrite(RT_STR_TUPLE(" at ")); + rtNoCrtFatalWritePtr(ASMReturnAddress()); + rtNoCrtFatalWriteEnd(RT_STR_TUPLE("!!\r\n\r\n")); +#endif +#if 0 + rtVccFatalSecurityErrorWithCtx(FAST_FAIL_FATAL_APP_EXIT, pCpuCtx); +#else +# ifdef IN_RING3 + if (g_pfnIsDebuggerPresent && g_pfnIsDebuggerPresent()) +# endif + RT_BREAKPOINT(); +#endif +} + + +void rtVccCheckContextFailed(PCONTEXT pCpuCtx) +{ +#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE + RTAssertMsg2("\n\n!!Context (stack) check failed!!\n\n" + "PC=%p SP=%p BP=%p\n", +# ifdef RT_ARCH_AMD64 + pCpuCtx->Rip, pCpuCtx->Rsp, pCpuCtx->Rbp +# elif defined(RT_ARCH_X86) + pCpuCtx->Eip, pCpuCtx->Esp, pCpuCtx->Ebp +# else +# error "unsupported arch" +# endif + ); +#else + rtNoCrtFatalWriteBegin(RT_STR_TUPLE("\r\n\r\n!!Context (stack) check failed!!\r\n\r\n" + "PC=")); +# ifdef RT_ARCH_AMD64 + rtNoCrtFatalWritePtr((void *)pCpuCtx->Rip); +# elif defined(RT_ARCH_X86) + rtNoCrtFatalWritePtr((void *)pCpuCtx->Eip); +# else +# error "unsupported arch" +# endif + rtNoCrtFatalWrite(RT_STR_TUPLE(" SP=")); +# ifdef RT_ARCH_AMD64 + rtNoCrtFatalWritePtr((void *)pCpuCtx->Rsp); +# elif defined(RT_ARCH_X86) + rtNoCrtFatalWritePtr((void *)pCpuCtx->Esp); +# endif + rtNoCrtFatalWrite(RT_STR_TUPLE(" BP=")); +# ifdef RT_ARCH_AMD64 + rtNoCrtFatalWritePtr((void *)pCpuCtx->Rbp); +# elif defined(RT_ARCH_X86) + rtNoCrtFatalWritePtr((void *)pCpuCtx->Ebp); +# endif + rtNoCrtFatalWriteEnd(RT_STR_TUPLE("\r\n")); +#endif + rtVccFatalSecurityErrorWithCtx(FAST_FAIL_INVALID_SET_OF_CONTEXT, pCpuCtx); +} + diff --git a/src/VBox/Runtime/common/compiler/vcc/tlsdir-vcc.c b/src/VBox/Runtime/common/compiler/vcc/tlsdir-vcc.c new file mode 100644 index 00000000..b151bbe5 --- /dev/null +++ b/src/VBox/Runtime/common/compiler/vcc/tlsdir-vcc.c @@ -0,0 +1,114 @@ +/* $Id: tlsdir-vcc.c $ */ +/** @file + * IPRT - Visual C++ Compiler - PE/Windows TLS Directory. + * + * @note Doing this as a C file for same reasons as loadcfg-vcc.c. + */ + +/* + * Copyright (C) 2022-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define IPRT_COMPILER_VCC_WITH_TLS_CALLBACK_SECTIONS +#define IPRT_COMPILER_VCC_WITH_TLS_DATA_SECTIONS +#include "internal/iprt.h" +#include <iprt/win/windows.h> + +#include "internal/compiler-vcc.h" + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** @name TLS callback arrays. + * + * The important thing here are the section names, the linker is told to merge + * and sort all the .CRT* sections into .rdata. + * + * @{ */ +/** Start of the TLS callback array. */ +__declspec(allocate(".CRT$XLA")) PIMAGE_TLS_CALLBACK g_apfnRTVccTlsCallbacks_Start[] = { NULL, }; +/** End of the TLS callback array (not actually used, but seems to be + * traditional). */ +__declspec(allocate(".CRT$XLZ")) PIMAGE_TLS_CALLBACK g_apfnRTVccTlsCallbacks_End[] = { NULL, }; + +/* Tell the linker to merge the .CRT* sections into .rdata */ +#pragma comment(linker, "/merge:.CRT=.rdata ") +/** @} */ + + +/** @name TLS data arrays. + * + * @{ */ +#pragma data_seg(".tls") + +/** Start of the TLS data. + * @note The linker has a reference to name '_tls_start' indicating a possible + * required naming convention here. + * @note Not sure if the byte here is ignored or not... In assembly we could + * optimize it out, I think... */ +extern __declspec(allocate(".tls")) char _tls_start = 0; + +/** End of the TLS callback array. + * @note The linker has a reference to name '_tls_end' indicating a possible + * required naming convention here. */ +extern __declspec(allocate(".tls$ZZZ")) char _tls_end = 0; + +#pragma data_seg () +/** @} */ + + +/** The TLS index for the module we're linked into. + * The linker has a reference to the name '_tls_start', so this is probably + * fixed in some way. */ +extern ULONG _tls_index = 0; + + +/** + * The TLS directory for the PE image. + * + * The name of this is dictated by the linker, as it looks for a _tls_used + * symbol and puts it's address and (somehow) size in the TLS data dir entry. + */ +extern __declspec(".rdata$T") /* seems to be tranditional, doubt it is necessary */ +const RT_CONCAT(IMAGE_TLS_DIRECTORY, ARCH_BITS) _tls_used = +{ + /* .StartAddressOfRawData = */ (uintptr_t)&_tls_start, + /* .EndAddressOfRawData = */ (uintptr_t)&_tls_end, + /* .AddressOfIndex = */ (uintptr_t)&_tls_index, + /* .AddressOfCallBacks = */ (uintptr_t)&g_apfnRTVccTlsCallbacks_Start[1], + /* .SizeOfZeroFill = */ 0, + /* .Characteristics = */ 0, +}; + |