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.
This commit is contained in:
Andrew Schwartzmeyer 2016-04-11 16:56:19 -07:00
parent ceef777d5e
commit 1bc2a9e576

View file

@ -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
;
/// <summary>