From 1bc2a9e576a9f0fbdbcc37a01a9d1f10964775d6 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Mon, 11 Apr 2016 16:56:19 -0700 Subject: [PATCH] Fix stack overflow exception when loading assemblies When an assembly that exists outside of the trusted platform assembly list but in the GAC on Windows was loaded, the use of `Assembly.Load` before the `LoadFromAssemblyPath` caused a recursive loop to occur. This happens because an `Assembly.Load` on a not-yet-loaded assembly is overridden, thus calling back into the `AssemblyLoadContext.Load` override. By attempting to load from the file path first, we avoid this loop. However, loading TPA assemblies by their path throws an exception, so now we catch this particular exception and attempt the load through `Assembly.Load`. This is safe for TPA assemblies since they're already loaded, thus the `Assembly.Load` does not re-load the assembly, but simply returns it. --- .../CoreCLR/CorePsAssemblyLoadContext.cs | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CoreCLR/CorePsAssemblyLoadContext.cs b/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CoreCLR/CorePsAssemblyLoadContext.cs index 8c17f5e86..106cf402a 100644 --- a/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CoreCLR/CorePsAssemblyLoadContext.cs +++ b/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CoreCLR/CorePsAssemblyLoadContext.cs @@ -141,9 +141,8 @@ namespace System.Management.Automation string asmCultureName = assemblyName.CultureName ?? string.Empty; string asmFilePath = null; - for (int i = 0; i < probingPaths.Count; i++) + foreach (var probingPath in probingPaths) { - string probingPath = probingPaths[i]; string asmCulturePath = Path.Combine(probingPath, asmCultureName); for (int k = 0; k < extensions.Length; k++) { @@ -184,19 +183,21 @@ namespace System.Management.Automation assemblyName.FullName); } - // Try loading it from the TPA list. All PowerShell dependencies are in the - // TPA list when published with dotnet-cli. try - { - asmLoaded = Assembly.Load(assemblyName); - } - // If it wasn't there, try loading from path - catch (FileNotFoundException) { asmLoaded = asmFilePath.EndsWith(".ni.dll", StringComparison.OrdinalIgnoreCase) ? LoadFromNativeImagePath(asmFilePath, null) : LoadFromAssemblyPath(asmFilePath); } + // Since .NET CLI built versions of PowerShell have all the + // built-in assemblies in the TPA list, the above will throw, + // and we have to use Assembly.Load. However, we must try the + // above first, otherwise assemblies that exist outside the TPA + // list will go into a recursive loop. + catch (System.IO.FileLoadException) + { + asmLoaded = System.Reflection.Assembly.Load(assemblyName); + } // If it loaded, add it to the cache if (asmLoaded != null) @@ -250,20 +251,22 @@ namespace System.Management.Automation if (TryGetAssemblyFromCache(assemblyName, out asmLoaded)) return asmLoaded; - // Try loading it from the TPA list. All PowerShell dependencies are in the - // TPA list when published with dotnet-cli. try - { - asmLoaded = Assembly.Load(assemblyName); - } - // If it wasn't there, try loading from path - catch (FileNotFoundException) { // Load the assembly through 'LoadFromNativeImagePath' or 'LoadFromAssemblyPath' asmLoaded = assemblyPath.EndsWith(".ni.dll", StringComparison.OrdinalIgnoreCase) ? LoadFromNativeImagePath(assemblyPath, null) : LoadFromAssemblyPath(assemblyPath); } + // Since .NET CLI built versions of PowerShell have all the + // built-in assemblies in the TPA list, the above will throw, + // and we have to use Assembly.Load. However, we must try the + // above first, otherwise assemblies that exist outside the TPA + // list will go into a recursive loop. + catch (System.IO.FileLoadException) + { + asmLoaded = System.Reflection.Assembly.Load(assemblyName); + } if (asmLoaded != null) { @@ -503,11 +506,11 @@ namespace System.Management.Automation // Porting note: it's much easier to send an LPStr on Linux private const UnmanagedType stringType = -#if LINUX + #if LINUX UnmanagedType.LPStr -#else + #else UnmanagedType.LPWStr -#endif + #endif ; ///