diff --git a/src/coreclr/gc/gcconfig.h b/src/coreclr/gc/gcconfig.h index f4b88d26db1ecf..d834225d2bad38 100644 --- a/src/coreclr/gc/gcconfig.h +++ b/src/coreclr/gc/gcconfig.h @@ -137,7 +137,8 @@ class GCConfigStringHolder INT_CONFIG (GCEnabledInstructionSets, "GCEnabledInstructionSets", NULL, -1, "Specifies whether GC can use AVX2 or AVX512F - 0 for neither, 1 for AVX2, 3 for AVX512F")\ INT_CONFIG (GCConserveMem, "GCConserveMemory", "System.GC.ConserveMemory", 0, "Specifies how hard GC should try to conserve memory - values 0-9") \ INT_CONFIG (GCWriteBarrier, "GCWriteBarrier", NULL, 0, "Specifies whether GC should use more precise but slower write barrier") \ - STRING_CONFIG(GCName, "GCName", "System.GC.Name", "Specifies the path of the standalone GC implementation.") \ + STRING_CONFIG(GCName, "GCName", "System.GC.Name", "Specifies the name of the standalone GC implementation.") \ + STRING_CONFIG(GCPath, "GCPath", "System.GC.Path", "Specifies the path of the standalone GC implementation.") \ INT_CONFIG (GCSpinCountUnit, "GCSpinCountUnit", NULL, 0, "Specifies the spin count unit used by the GC.") \ INT_CONFIG (GCDynamicAdaptationMode, "GCDynamicAdaptationMode", "System.GC.DynamicAdaptationMode", 0, "Enable the GC to dynamically adapt to application sizes.") // This class is responsible for retreiving configuration information diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 33586e15dcb512..608cbc40d620db 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -284,6 +284,7 @@ CONFIG_DWORD_INFO(INTERNAL_GcStressOnDirectCalls, W("GcStressOnDirectCalls"), 0, RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_HeapVerify, W("HeapVerify"), 0, "When set verifies the integrity of the managed heap on entry and exit of each GC") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_GCCpuGroup, W("GCCpuGroup"), 0, "Specifies if to enable GC to support CPU groups") RETAIL_CONFIG_STRING_INFO(EXTERNAL_GCName, W("GCName"), "") +RETAIL_CONFIG_STRING_INFO(EXTERNAL_GCPath, W("GCPath"), "") /** * This flag allows us to force the runtime to use global allocation context on Windows x86/amd64 instead of thread allocation context just for testing purpose. * The flag is unsafe for a subtle reason. Although the access to the g_global_alloc_context is protected under a lock. The implementation of diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h index 01f4da28d47939..bedd52d3b6d555 100644 --- a/src/coreclr/inc/utilcode.h +++ b/src/coreclr/inc/utilcode.h @@ -55,6 +55,8 @@ #define CoreLibSatelliteName_A "System.Private.CoreLib.resources" #define CoreLibSatelliteNameLen 32 +bool ValidateModuleName(LPCWSTR pwzModuleName); + class StringArrayList; #if !defined(_DEBUG_IMPL) && defined(_DEBUG) && !defined(DACCESS_COMPILE) diff --git a/src/coreclr/nativeaot/Runtime/clrgc.enabled.cpp b/src/coreclr/nativeaot/Runtime/clrgc.enabled.cpp index d35127dc1b0e77..c552db3f494a59 100644 --- a/src/coreclr/nativeaot/Runtime/clrgc.enabled.cpp +++ b/src/coreclr/nativeaot/Runtime/clrgc.enabled.cpp @@ -58,52 +58,111 @@ HRESULT InitializeStandaloneGC() return GCHeapUtilities::InitializeStandaloneGC(); } +// Validate that the name used to load the GC is just a simple file name +// and does not contain something that could be used in a non-qualified path. +// For example, using the string "..\..\..\clrgc.dll" we might attempt to +// load a GC from the root of the drive. +// +// The minimal set of characters that we must check for and exclude are: +// On all platforms: +// '/' - (forward slash) +// On Windows: +// '\\' - (backslash) +// ':' - (colon) +// +// Returns false if we find any of these characters in 'pwzModuleName' +// Returns true if we reach the null terminator without encountering +// any of these characters. +// +bool ValidateModuleName(const char* pwzModuleName) +{ + const char* pCurChar = pwzModuleName; + char curChar; + do { + curChar = *pCurChar; + if (curChar == '/' +#ifdef TARGET_WINDOWS + || (curChar == '\\') || (curChar == ':') +#endif + ) + { + // Return false if we find any of these character in 'pwzJitName' + return false; + } + pCurChar++; + } while (curChar != 0); + + // Return true; we have reached the null terminator + // + return true; +} + HRESULT GCHeapUtilities::InitializeStandaloneGC() { char* moduleName; + char* modulePath; + + if (!RhConfig::Environment::TryGetStringValue("GCPath", &modulePath)) + { + modulePath = nullptr; + } if (!RhConfig::Environment::TryGetStringValue("GCName", &moduleName)) { - return GCHeapUtilities::InitializeDefaultGC(); + moduleName = nullptr; } - NewArrayHolder moduleNameHolder(moduleName); - HANDLE executableModule = PalGetModuleHandleFromPointer((void*)&PalGetModuleHandleFromPointer); - const TCHAR * executableModulePath = NULL; - PalGetModuleFileName(&executableModulePath, executableModule); - char* convertedExecutableModulePath = PalCopyTCharAsChar(executableModulePath); - if (!convertedExecutableModulePath) + if (!(moduleName || modulePath)) { - return E_OUTOFMEMORY; + return GCHeapUtilities::InitializeDefaultGC(); } - NewArrayHolder convertedExecutableModulePathHolder(convertedExecutableModulePath); + + NewArrayHolder moduleNameHolder(moduleName); + if (!modulePath) { - char* p = convertedExecutableModulePath; - char* q = nullptr; - while (*p != '\0') + if (!ValidateModuleName(moduleName)) + { + LOG((LF_GC, LL_FATALERROR, "GC initialization failed to load the Standalone GC library.\n")); + return E_FAIL; + } + + HANDLE executableModule = PalGetModuleHandleFromPointer((void*)&PalGetModuleHandleFromPointer); + const TCHAR * executableModulePath = NULL; + PalGetModuleFileName(&executableModulePath, executableModule); + char* convertedExecutableModulePath = PalCopyTCharAsChar(executableModulePath); + if (!convertedExecutableModulePath) + { + return E_OUTOFMEMORY; + } + NewArrayHolder convertedExecutableModulePathHolder(convertedExecutableModulePath); { - if (*p == DIRECTORY_SEPARATOR_CHAR) + char* p = convertedExecutableModulePath; + char* q = nullptr; + while (*p != '\0') { - q = p; + if (*p == DIRECTORY_SEPARATOR_CHAR) + { + q = p; + } + p++; } - p++; + assert(q != nullptr); + q++; + *q = '\0'; } - assert(q != nullptr); - q++; - *q = '\0'; - } - size_t folderLength = strlen(convertedExecutableModulePath); - size_t nameLength = strlen(moduleName); - char* moduleFullPath = new (nothrow) char[folderLength + nameLength + 1]; - if (!moduleFullPath) - { - return E_OUTOFMEMORY; + size_t folderLength = strlen(convertedExecutableModulePath); + size_t nameLength = strlen(moduleName); + modulePath = new (nothrow) char[folderLength + nameLength + 1]; + if (!modulePath) + { + return E_OUTOFMEMORY; + } + strcpy(modulePath, convertedExecutableModulePath); + strcpy(modulePath + folderLength, moduleName); } - NewArrayHolder moduleFullPathHolder(moduleFullPath); - strcpy(moduleFullPath, convertedExecutableModulePath); - strcpy(moduleFullPath + folderLength, moduleName); - HANDLE hMod = PalLoadLibrary(moduleFullPath); + NewArrayHolder modulePathHolder(modulePath); + HANDLE hMod = PalLoadLibrary(modulePath); if (!hMod) { diff --git a/src/coreclr/utilcode/util.cpp b/src/coreclr/utilcode/util.cpp index 1819f38e0a4381..88317684fe882b 100644 --- a/src/coreclr/utilcode/util.cpp +++ b/src/coreclr/utilcode/util.cpp @@ -32,6 +32,45 @@ bool g_arm64_atomics_present = false; #endif //!DACCESS_COMPILE +// Validate that the name used to load the JIT/GC is just a simple file name +// and does not contain something that could be used in a non-qualified path. +// For example, using the string "..\..\..\myjit.dll" we might attempt to +// load a JIT from the root of the drive. +// +// The minimal set of characters that we must check for and exclude are: +// On all platforms: +// '/' - (forward slash) +// On Windows: +// '\\' - (backslash) +// ':' - (colon) +// +// Returns false if we find any of these characters in 'pwzModuleName' +// Returns true if we reach the null terminator without encountering +// any of these characters. +// +bool ValidateModuleName(LPCWSTR pwzModuleName) +{ + LPCWSTR pCurChar = pwzModuleName; + wchar_t curChar; + do { + curChar = *pCurChar; + if (curChar == '/' +#ifdef TARGET_WINDOWS + || (curChar == '\\') || (curChar == ':') +#endif + ) + { + // Return false if we find any of these character in 'pwzJitName' + return false; + } + pCurChar++; + } while (curChar != 0); + + // Return true; we have reached the null terminator + // + return true; +} + //***************************************************************************** // Convert a string of hex digits into a hex value of the specified # of bytes. //***************************************************************************** diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index ff096681a07421..0885ec6d3b8ede 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -1669,45 +1669,6 @@ struct JIT_LOAD_DATA // Here's the global data for JIT load and initialization state. JIT_LOAD_DATA g_JitLoadData; -// Validate that the name used to load the JIT is just a simple file name -// and does not contain something that could be used in a non-qualified path. -// For example, using the string "..\..\..\myjit.dll" we might attempt to -// load a JIT from the root of the drive. -// -// The minimal set of characters that we must check for and exclude are: -// On all platforms: -// '/' - (forward slash) -// On Windows: -// '\\' - (backslash) -// ':' - (colon) -// -// Returns false if we find any of these characters in 'pwzJitName' -// Returns true if we reach the null terminator without encountering -// any of these characters. -// -static bool ValidateJitName(LPCWSTR pwzJitName) -{ - LPCWSTR pCurChar = pwzJitName; - wchar_t curChar; - do { - curChar = *pCurChar; - if (curChar == '/' -#ifdef TARGET_WINDOWS - || (curChar == '\\') || (curChar == ':') -#endif - ) - { - // Return false if we find any of these character in 'pwzJitName' - return false; - } - pCurChar++; - } while (curChar != 0); - - // Return true; we have reached the null terminator - // - return true; -} - CORINFO_OS getClrVmOs(); #define LogJITInitializationError(...) \ @@ -1770,7 +1731,7 @@ static void LoadAndInitializeJIT(LPCWSTR pwzJitName DEBUGARG(LPCWSTR pwzJitPath) return; } - if (ValidateJitName(pwzJitName)) + if (ValidateModuleName(pwzJitName)) { // Load JIT from next to CoreCLR binary PathString CoreClrFolderHolder; diff --git a/src/coreclr/vm/gcheaputilities.cpp b/src/coreclr/vm/gcheaputilities.cpp index 1eeca5d999db65..618d9f6a8b7220 100644 --- a/src/coreclr/vm/gcheaputilities.cpp +++ b/src/coreclr/vm/gcheaputilities.cpp @@ -5,6 +5,7 @@ #include "configuration.h" #include "gcheaputilities.h" #include "appdomain.hpp" +#include "hostinformation.h" #include "../gc/env/gcenv.ee.h" #include "../gc/env/gctoeeinterface.standalone.inl" @@ -85,7 +86,6 @@ PTR_VOID GCHeapUtilities::GetGCModuleBase() namespace { - // This block of code contains all of the state necessary to handle incoming // EtwCallbacks before the GC has been initialized. This is a tricky problem // because EtwCallbacks can appear at any time, even when we are just about @@ -161,18 +161,47 @@ void StashKeywordAndLevel(bool isPublicProvider, GCEventKeyword keywords, GCEven } #ifdef FEATURE_STANDALONE_GC -HMODULE LoadStandaloneGc(LPCWSTR libFileName) +HMODULE LoadStandaloneGc(LPCWSTR libFileName, LPCWSTR libFilePath) { LIMITED_METHOD_CONTRACT; + HMODULE result = nullptr; + + if (libFilePath) + { + return CLRLoadLibrary(libFilePath); + } - // Look for the standalone GC module next to the clr binary - PathString libPath = GetInternalSystemDirectory(); - libPath.Append(libFileName); + if (!ValidateModuleName(libFileName)) + { + LOG((LF_GC, LL_INFO100, "Invalid GC name found %s\n", libFileName)); + return nullptr; + } + + SString appBase; + if (HostInformation::GetProperty("APP_CONTEXT_BASE_DIRECTORY", appBase)) + { + PathString libPath = appBase.GetUnicode(); + libPath.Append(libFileName); - LOG((LF_GC, LL_INFO100, "Loading standalone GC from path %s\n", libPath.GetUTF8())); + LOG((LF_GC, LL_INFO100, "Loading standalone GC from appBase %s\n", libPath.GetUTF8())); + + LPCWSTR libraryName = libPath.GetUnicode(); + result = CLRLoadLibrary(libraryName); + } + + if (result == nullptr) + { + // Look for the standalone GC module next to the clr binary + PathString libPath = GetInternalSystemDirectory(); + libPath.Append(libFileName); + + LOG((LF_GC, LL_INFO100, "Loading standalone GC by coreclr %s\n", libPath.GetUTF8())); + + LPCWSTR libraryName = libPath.GetUnicode(); + result = CLRLoadLibrary(libraryName); + } - LPCWSTR libraryName = libPath.GetUnicode(); - return CLRLoadLibrary(libraryName); + return result; } #endif // FEATURE_STANDALONE_GC @@ -182,7 +211,7 @@ HMODULE LoadStandaloneGc(LPCWSTR libFileName) // // See Documentation/design-docs/standalone-gc-loading.md for details // on the loading protocol in use here. -HRESULT LoadAndInitializeGC(LPCWSTR standaloneGcLocation) +HRESULT LoadAndInitializeGC(LPCWSTR standaloneGCName, LPCWSTR standaloneGCPath) { LIMITED_METHOD_CONTRACT; @@ -190,13 +219,16 @@ HRESULT LoadAndInitializeGC(LPCWSTR standaloneGcLocation) LOG((LF_GC, LL_FATALERROR, "EE not built with the ability to load standalone GCs")); return E_FAIL; #else - HMODULE hMod = LoadStandaloneGc(standaloneGcLocation); + HMODULE hMod = LoadStandaloneGc(standaloneGCName, standaloneGCPath); if (!hMod) { HRESULT err = GetLastError(); #ifdef LOGGING - MAKE_UTF8PTR_FROMWIDE(standaloneGcLocationUtf8, standaloneGcLocation); - LOG((LF_GC, LL_FATALERROR, "Load of %s failed\n", standaloneGcLocationUtf8)); + LPCWSTR standaloneGCNameLogging = standaloneGCName ? standaloneGCName : W(""); + LPCWSTR standaloneGCPathLogging = standaloneGCPath ? standaloneGCPath : W(""); + MAKE_UTF8PTR_FROMWIDE(standaloneGCNameUtf8, standaloneGCNameLogging); + MAKE_UTF8PTR_FROMWIDE(standaloneGCPathUtf8, standaloneGCPathLogging); + LOG((LF_GC, LL_FATALERROR, "Load of %s or %s failed\n", standaloneGCNameUtf8, standaloneGCPathUtf8)); #endif // LOGGING return __HRESULT_FROM_WIN32(err); } @@ -344,16 +376,17 @@ HRESULT GCHeapUtilities::LoadAndInitialize() assert(g_gc_load_status == GC_LOAD_STATUS_BEFORE_START); g_gc_load_status = GC_LOAD_STATUS_START; - LPCWSTR standaloneGcLocation = Configuration::GetKnobStringValue(W("System.GC.Name"), CLRConfig::EXTERNAL_GCName); + LPCWSTR standaloneGCName = Configuration::GetKnobStringValue(W("System.GC.Name"), CLRConfig::EXTERNAL_GCName); + LPCWSTR standaloneGCPath = Configuration::GetKnobStringValue(W("System.GC.Path"), CLRConfig::EXTERNAL_GCPath); g_gc_dac_vars.major_version_number = GC_INTERFACE_MAJOR_VERSION; g_gc_dac_vars.minor_version_number = GC_INTERFACE_MINOR_VERSION; - if (!standaloneGcLocation) + if (!standaloneGCName && !standaloneGCPath) { return InitializeDefaultGC(); } else { - return LoadAndInitializeGC(standaloneGcLocation); + return LoadAndInitializeGC(standaloneGCName, standaloneGCPath); } }