diff --git a/src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.cs index 8bbd6e98ddaabb..73d6795985a8ea 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.cs @@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Security; using System.Threading; namespace System @@ -33,12 +34,15 @@ public static extern int ExitCode set; } - // Note: The CLR's Watson bucketization code looks at the caller of the FCALL method - // to assign blame for crashes. Don't mess with this, such as by making it call - // another managed helper method, unless you consult with some CLR Watson experts. [DoesNotReturn] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void FailFast(string? message); + [DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod + public static void FailFast(string? message) + { + // Note: The CLR's Watson bucketization code looks at the our caller + // to assign blame for crashes. + StackCrawlMark mark = StackCrawlMark.LookForMyCaller; + FailFast(ref mark, message, exception: null, errorMessage: null); + } // This overload of FailFast will allow you to specify the exception object // whose bucket details *could* be used when undergoing the failfast process. @@ -54,12 +58,34 @@ public static extern int ExitCode // IP for bucketing. If the exception object is not preallocated, it will use the bucket // details contained in the object (if any). [DoesNotReturn] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void FailFast(string? message, Exception? exception); + [DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod + public static void FailFast(string? message, Exception? exception) + { + // Note: The CLR's Watson bucketization code looks at the our caller + // to assign blame for crashes. + StackCrawlMark mark = StackCrawlMark.LookForMyCaller; + FailFast(ref mark, message, exception, errorMessage: null); + } + + [DoesNotReturn] + [DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod + internal static void FailFast(string? message, Exception? exception, string? errorMessage) + { + // Note: The CLR's Watson bucketization code looks at the our caller + // to assign blame for crashes. + StackCrawlMark mark = StackCrawlMark.LookForMyCaller; + FailFast(ref mark, message, exception, errorMessage); + } + + [DoesNotReturn] + private static void FailFast(ref StackCrawlMark mark, string? message, Exception? exception, string? errorMessage) + { + FailFast(new StackCrawlMarkHandle(ref mark), new StringHandleOnStack(ref message), ObjectHandleOnStack.Create(ref exception), new StringHandleOnStack(ref errorMessage)); + } + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Environment_FailFast")] [DoesNotReturn] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void FailFast(string? message, Exception? exception, string? errorMessage); + private static partial void FailFast(StackCrawlMarkHandle mark, StringHandleOnStack message, ObjectHandleOnStack exception, StringHandleOnStack errorMessage); private static unsafe string[] InitializeCommandLineArgs(char* exePath, int argc, char** argv) // invoked from VM { diff --git a/src/coreclr/classlibnative/bcltype/system.cpp b/src/coreclr/classlibnative/bcltype/system.cpp index 6e9a9a9ee956a5..0d6091387a97f6 100644 --- a/src/coreclr/classlibnative/bcltype/system.cpp +++ b/src/coreclr/classlibnative/bcltype/system.cpp @@ -133,6 +133,38 @@ extern "C" INT32 QCALLTYPE Environment_GetProcessorCount() return processorCount; } +struct FindFailFastCallerStruct { + StackCrawlMark* pStackMark; + UINT_PTR retAddress; +}; + +// This method is called by the GetMethod function and will crawl backward +// up the stack for integer methods. +static StackWalkAction FindFailFastCallerCallback(CrawlFrame* frame, VOID* data) { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + FindFailFastCallerStruct* pFindCaller = (FindFailFastCallerStruct*) data; + + // The check here is between the address of a local variable + // (the stack mark) and a pointer to the EIP for a frame + // (which is actually the pointer to the return address to the + // function from the previous frame). So we'll actually notice + // which frame the stack mark was in one frame later. This is + // fine since we only implement LookForMyCaller. + _ASSERTE(*pFindCaller->pStackMark == LookForMyCaller); + if (!frame->IsInCalleesFrames(pFindCaller->pStackMark)) + return SWA_CONTINUE; + + pFindCaller->retAddress = GetControlPC(frame->GetRegisterSet()); + return SWA_ABORT; +} + // FailFast is supported in BCL.small as internal to support failing fast in places where EEE used to be thrown. // // Static message buffer used by SystemNative::FailFast to avoid reliance on a @@ -143,9 +175,10 @@ WCHAR *g_pFailFastBuffer = g_szFailFastBuffer; #define FAIL_FAST_STATIC_BUFFER_LENGTH (sizeof(g_szFailFastBuffer) / sizeof(WCHAR)) -// This is the common code for FailFast processing that is wrapped by the two + +// This is the common code for FailFast processing that is wrapped by the // FailFast FCalls below. -void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, UINT_PTR retAddress, STRINGREF refErrorSourceString) +void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, StackCrawlMark* stackMark, STRINGREF refErrorSourceString) { CONTRACTL { @@ -166,6 +199,11 @@ void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExce GCPROTECT_BEGIN(gc); + FindFailFastCallerStruct findCallerData; + findCallerData.pStackMark = stackMark; + findCallerData.retAddress = 0; + StackWalkFunctions(GetThread(), FindFailFastCallerCallback, &findCallerData); + // Managed code injected FailFast maps onto the unmanaged version // (EEPolicy::HandleFatalError) in the following manner: the exit code is // always set to COR_E_FAILFAST and the address passed (usually a failing @@ -267,7 +305,7 @@ void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExce { PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = pThread->GetExceptionState()->GetUEWatsonBucketTracker(); _ASSERTE(pUEWatsonBucketTracker != NULL); - pUEWatsonBucketTracker->SaveIpForWatsonBucket(retAddress); + pUEWatsonBucketTracker->SaveIpForWatsonBucket(findCallerData.retAddress); pUEWatsonBucketTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::FatalError, pThread, NULL); if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() == NULL) { @@ -282,69 +320,28 @@ void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExce if (gc.refExceptionForWatsonBucketing != NULL) pThread->SetLastThrownObject(gc.refExceptionForWatsonBucketing); - EEPolicy::HandleFatalError(COR_E_FAILFAST, retAddress, pszMessage, NULL, errorSourceString, argExceptionString); + EEPolicy::HandleFatalError(COR_E_FAILFAST, findCallerData.retAddress, pszMessage, NULL, errorSourceString, argExceptionString); GCPROTECT_END(); } -// Note: Do not merge this FCALL method with any other FailFast overloads. -// Watson uses the managed FailFast method with one String for crash dump bucketization. -FCIMPL1(VOID, SystemNative::FailFast, StringObject* refMessageUNSAFE) -{ - FCALL_CONTRACT; - - STRINGREF refMessage = (STRINGREF)refMessageUNSAFE; - - HELPER_METHOD_FRAME_BEGIN_1(refMessage); - - // The HelperMethodFrame knows how to get the return address. - UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS(); - - // Call the actual worker to perform failfast - GenericFailFast(refMessage, NULL, retaddr, NULL); - - HELPER_METHOD_FRAME_END(); -} -FCIMPLEND - -FCIMPL2(VOID, SystemNative::FailFastWithException, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE) -{ - FCALL_CONTRACT; - - STRINGREF refMessage = (STRINGREF)refMessageUNSAFE; - EXCEPTIONREF refException = (EXCEPTIONREF)refExceptionUNSAFE; - - HELPER_METHOD_FRAME_BEGIN_2(refMessage, refException); - - // The HelperMethodFrame knows how to get the return address. - UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS(); - - // Call the actual worker to perform failfast - GenericFailFast(refMessage, refException, retaddr, NULL); - - HELPER_METHOD_FRAME_END(); -} -FCIMPLEND - -FCIMPL3(VOID, SystemNative::FailFastWithExceptionAndSource, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE, StringObject* errorSourceUNSAFE) +extern "C" void QCALLTYPE Environment_FailFast(QCall::StackCrawlMarkHandle mark, QCall::StringHandleOnStack message, QCall::ObjectHandleOnStack exception, QCall::StringHandleOnStack errorSource) { - FCALL_CONTRACT; - - STRINGREF refMessage = (STRINGREF)refMessageUNSAFE; - EXCEPTIONREF refException = (EXCEPTIONREF)refExceptionUNSAFE; - STRINGREF errorSource = (STRINGREF)errorSourceUNSAFE; + QCALL_CONTRACT; - HELPER_METHOD_FRAME_BEGIN_3(refMessage, refException, errorSource); + BEGIN_QCALL; - // The HelperMethodFrame knows how to get the return address. - UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS(); + GCX_COOP(); + + STRINGREF refMessage = message.Get(); + EXCEPTIONREF refException = (EXCEPTIONREF)exception.Get(); + STRINGREF refErrorSource = errorSource.Get(); // Call the actual worker to perform failfast - GenericFailFast(refMessage, refException, retaddr, errorSource); + SystemNative::GenericFailFast(refMessage, refException, mark, refErrorSource); - HELPER_METHOD_FRAME_END(); + END_QCALL; } -FCIMPLEND FCIMPL0(FC_BOOL_RET, SystemNative::IsServerGC) { diff --git a/src/coreclr/classlibnative/bcltype/system.h b/src/coreclr/classlibnative/bcltype/system.h index e440f1fa8b0672..c7dae76de9789a 100644 --- a/src/coreclr/classlibnative/bcltype/system.h +++ b/src/coreclr/classlibnative/bcltype/system.h @@ -43,22 +43,19 @@ class SystemNative static FCDECL1(VOID,SetExitCode,INT32 exitcode); static FCDECL0(INT32, GetExitCode); - static FCDECL1(VOID, FailFast, StringObject* refMessageUNSAFE); - static FCDECL2(VOID, FailFastWithException, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE); - static FCDECL3(VOID, FailFastWithExceptionAndSource, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE, StringObject* errorSourceUNSAFE); - static FCDECL0(FC_BOOL_RET, IsServerGC); // Return a method info for the method were the exception was thrown static FCDECL1(ReflectMethodObject*, GetMethodFromStackTrace, ArrayBase* pStackTraceUNSAFE); - -private: + // Common processing code for FailFast - static void GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, UINT_PTR retAddress, STRINGREF errorSource); + static void GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, StackCrawlMark* stackCrawlMark, STRINGREF errorSource); }; extern "C" void QCALLTYPE Environment_Exit(INT32 exitcode); +extern "C" void QCALLTYPE Environment_FailFast(QCall::StackCrawlMarkHandle mark, QCall::StringHandleOnStack message, QCall::ObjectHandleOnStack exception, QCall::StringHandleOnStack errorSource); + // Returns the number of logical processors that can be used by managed code extern "C" INT32 QCALLTYPE Environment_GetProcessorCount(); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 9da9ebdb102a6f..228a0d50f37502 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -93,10 +93,6 @@ FCFuncStart(gEnvironmentFuncs) FCFuncElement("get_TickCount64", SystemNative::GetTickCount64) FCFuncElement("set_ExitCode", SystemNative::SetExitCode) FCFuncElement("get_ExitCode", SystemNative::GetExitCode) - - FCFuncElementSig("FailFast", &gsig_SM_Str_RetVoid, SystemNative::FailFast) - FCFuncElementSig("FailFast", &gsig_SM_Str_Exception_RetVoid, SystemNative::FailFastWithException) - FCFuncElementSig("FailFast", &gsig_SM_Str_Exception_Str_RetVoid, SystemNative::FailFastWithExceptionAndSource) FCFuncEnd() FCFuncStart(gExceptionFuncs) diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 2d6466fc7d7c75..d546af82cff02e 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -94,6 +94,7 @@ static const Entry s_QCall[] = DllImportEntry(Delegate_FindMethodHandle) DllImportEntry(Delegate_InternalEqualMethodHandles) DllImportEntry(Environment_Exit) + DllImportEntry(Environment_FailFast) DllImportEntry(Environment_GetProcessorCount) DllImportEntry(ExceptionNative_GetMessageFromNativeResources) DllImportEntry(RuntimeTypeHandle_CreateInstanceForAnotherGenericParameter)