Harden AssemblyDependencyResolver assemblyPaths (#42055)
authorLakshan Fernando <lakshanf@hotmail.com>
Thu, 17 Sep 2020 12:58:36 +0000 (05:58 -0700)
committerGitHub <noreply@github.com>
Thu, 17 Sep 2020 12:58:36 +0000 (05:58 -0700)
* Harden AssemblyDependencyResolver assemblyPaths  AssemblyDependencyResolver is made resilience to the case where  hostpolicy.dll returns the same assembly paths by ignoring multiple  entries. This is done by using OrdinalIgnoreCase comparison on Windows and Ordinal comparison elsewhere  Fix #37162

* Pick the first simple assembly for multiples

AssemblyDependencyResolver is made resilience to the case when
hostpolicy.dll returns multiple assembly paths for the same assembly
by picking the first one.

Fix #37162

* missed an extra line

* Test case for casing change with the same assembly

* typo

* Disabled the tests for non-windows

Filed #42334 to track the linux-mac differences

Co-authored-by: Lakshan Fernando <lakshanf@microsoft.com>
src/installer/tests/HostActivation.Tests/DependencyResolution/ResolveComponentDependencies.cs
src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyDependencyResolver.cs

index d197943..cdd33ce 100644 (file)
@@ -50,6 +50,131 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.DependencyResolution
         }
 
         [Fact]
+        public void ComponentWithNoDependenciesCaseChangedOnAsm()
+        {
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                // Remove once https://github.com/dotnet/runtime/issues/42334 is resolved
+                return;
+            }
+
+            var component = sharedTestState.ComponentWithNoDependencies.Copy();
+
+            // Change the case of the first letter of AppDll
+            string fileName = component.AppDll;
+            string nameWOExtension = Path.GetFileNameWithoutExtension(fileName);
+            string nameWOExtensionCaseChanged = (Char.IsUpper(nameWOExtension[0]) ? nameWOExtension[0].ToString().ToLower() : nameWOExtension[0].ToString().ToUpper()) + nameWOExtension.Substring(1);
+            string changeFile = Path.Combine(Path.GetDirectoryName(fileName), (nameWOExtensionCaseChanged + Path.GetExtension(fileName)));
+
+            // Rename
+            File.Move(fileName, changeFile);
+
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                sharedTestState.RunComponentResolutionTest(component)
+                    .Should().Pass()
+                    .And.HaveStdOutContaining("corehost_resolve_component_dependencies:Success")
+                    .And.HaveStdOutContaining($"corehost_resolve_component_dependencies assemblies:[{component.AppDll}{Path.PathSeparator}]")
+                    .And.HaveStdErrContaining($"app_root='{component.Location}{Path.DirectorySeparatorChar}'")
+                    .And.HaveStdErrContaining($"deps='{component.DepsJson}'")
+                    .And.HaveStdErrContaining($"mgd_app='{component.AppDll}'");
+            }
+            else
+            {
+                // See https://github.com/dotnet/runtime/issues/42334
+                // We expect the test to fail due to the the case change of AppDll
+                sharedTestState.RunComponentResolutionTest(component)
+                    .Should().Pass()
+                    .And.HaveStdErrContaining($"Failed to locate managed application [{component.AppDll}]");
+            }
+        }
+
+        [Fact]
+        public void ComponentWithNoDependenciesCaseChangedOnDepsAndAsm()
+        {
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                // Remove once https://github.com/dotnet/runtime/issues/42334 is resolved
+                return;
+            }
+
+            var component = sharedTestState.ComponentWithNoDependencies.Copy();
+
+            // Change the case of the first letter of AppDll
+            string fileName = component.AppDll;
+            string nameWOExtension = Path.GetFileNameWithoutExtension(fileName);
+            string nameWOExtensionCaseChanged = (Char.IsUpper(nameWOExtension[0]) ? nameWOExtension[0].ToString().ToLower() : nameWOExtension[0].ToString().ToUpper()) + nameWOExtension.Substring(1);
+            string changeFile = Path.Combine(Path.GetDirectoryName(fileName), (nameWOExtensionCaseChanged + Path.GetExtension(fileName)));
+
+            string changeDepsFile = Path.Combine(Path.GetDirectoryName(component.DepsJson), (nameWOExtensionCaseChanged + ".deps" + Path.GetExtension(component.DepsJson)));
+
+            // Rename
+            File.Move(fileName, changeFile);
+            File.Move(component.DepsJson, changeDepsFile);
+
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                sharedTestState.RunComponentResolutionTest(component)
+                    .Should().Pass()
+                    .And.HaveStdOutContaining("corehost_resolve_component_dependencies:Success")
+                    .And.HaveStdOutContaining($"corehost_resolve_component_dependencies assemblies:[{component.AppDll}{Path.PathSeparator}]")
+                    .And.HaveStdErrContaining($"app_root='{component.Location}{Path.DirectorySeparatorChar}'")
+                    .And.HaveStdErrContaining($"deps='{component.DepsJson}'")
+                    .And.HaveStdErrContaining($"mgd_app='{component.AppDll}'");
+            }
+            else
+            {
+                // See https://github.com/dotnet/runtime/issues/42334
+                // We expect the test to fail due to the the case change of AppDll
+                sharedTestState.RunComponentResolutionTest(component)
+                    .Should().Pass()
+                    .And.HaveStdErrContaining($"Failed to locate managed application [{component.AppDll}]");
+            }
+        }
+
+        [Fact]
+        public void ComponentWithNoDependenciesNoDepsCaseChangedOnAsm()
+        {
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                // Remove once https://github.com/dotnet/runtime/issues/42334 is resolved
+                return;
+            }
+
+            var component = sharedTestState.ComponentWithNoDependencies.Copy();
+
+            // Change the case of the first letter of AppDll
+            string fileName = component.AppDll;
+            string nameWOExtension = Path.GetFileNameWithoutExtension(fileName);
+            string nameWOExtensionCaseChanged = (Char.IsUpper(nameWOExtension[0]) ? nameWOExtension[0].ToString().ToLower() : nameWOExtension[0].ToString().ToUpper()) + nameWOExtension.Substring(1);
+            string changeFile = Path.Combine(Path.GetDirectoryName(fileName), (nameWOExtensionCaseChanged + Path.GetExtension(fileName)));
+
+            // Rename
+            File.Move(fileName, changeFile);
+            // Delete deps
+            File.Delete(component.DepsJson);
+
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                sharedTestState.RunComponentResolutionTest(component)
+                    .Should().Pass()
+                    .And.HaveStdOutContaining("corehost_resolve_component_dependencies:Success")
+                    .And.HaveStdOutContaining($"corehost_resolve_component_dependencies assemblies:[{component.AppDll}{Path.PathSeparator}{changeFile}{Path.PathSeparator}]")
+                    .And.HaveStdErrContaining($"app_root='{component.Location}{Path.DirectorySeparatorChar}'")
+                    .And.HaveStdErrContaining($"deps='{component.DepsJson}'")
+                    .And.HaveStdErrContaining($"mgd_app='{component.AppDll}'");
+            }
+            else
+            {
+                // See https://github.com/dotnet/runtime/issues/42334
+                // We expect the test to fail due to the the case change of AppDll
+                sharedTestState.RunComponentResolutionTest(component)
+                    .Should().Pass()
+                    .And.HaveStdErrContaining($"Failed to locate managed application [{component.AppDll}]");
+            }
+        }
+
+        [Fact]
         public void ComponentWithNoDependencies()
         {
             sharedTestState.RunComponentResolutionTest(sharedTestState.ComponentWithNoDependencies)
index 5fa6cb5..edb2d8c 100644 (file)
@@ -95,7 +95,9 @@ namespace System.Runtime.Loader
             _assemblyPaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
             foreach (string assemblyPath in assemblyPaths)
             {
-                _assemblyPaths.Add(Path.GetFileNameWithoutExtension(assemblyPath), assemblyPath);
+                // Add the first entry with the same simple assembly name if there are multiples
+                // and ignore others
+                _assemblyPaths.TryAdd(Path.GetFileNameWithoutExtension(assemblyPath), assemblyPath);
             }
 
             _nativeSearchPaths = SplitPathsList(nativeSearchPathsList);