Skip to content

Commit adc21d8

Browse files
committed
Preventing unification on private host assemblies
1 parent 3ecf60d commit adc21d8

File tree

3 files changed

+60
-28
lines changed

3 files changed

+60
-28
lines changed

src/WebJobs.Script/Description/DotNet/FunctionAssemblyLoadContext.cs

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ namespace Microsoft.Azure.WebJobs.Script.Description
2222
/// </summary>
2323
public partial class FunctionAssemblyLoadContext : AssemblyLoadContext
2424
{
25+
private const string PrivateDependencyResolutionPolicy = "private";
26+
2527
private static readonly Lazy<Dictionary<string, ScriptRuntimeAssembly>> _runtimeAssemblies = new Lazy<Dictionary<string, ScriptRuntimeAssembly>>(DependencyHelper.GetRuntimeAssemblies);
2628
private static readonly Lazy<Dictionary<string, ResolutionPolicyEvaluator>> _resolutionPolicyEvaluators = new Lazy<Dictionary<string, ResolutionPolicyEvaluator>>(InitializeLoadPolicyEvaluators);
2729
private static readonly ConcurrentDictionary<string, object> _sharedContextAssembliesInFallbackLoad = new ConcurrentDictionary<string, object>();
@@ -128,7 +130,8 @@ private static Dictionary<string, ResolutionPolicyEvaluator> InitializeLoadPolic
128130
return new Dictionary<string, ResolutionPolicyEvaluator>
129131
{
130132
{ "minorMatchOrLower", IsMinorMatchOrLowerPolicyEvaluator },
131-
{ "runtimeVersion", RuntimeVersionPolicyEvaluator }
133+
{ "runtimeVersion", RuntimeVersionPolicyEvaluator },
134+
{ PrivateDependencyResolutionPolicy, (a, b) => false }
132135
};
133136
}
134137

@@ -194,31 +197,42 @@ protected override Assembly Load(AssemblyName assemblyName)
194197
return assembly;
195198
}
196199

197-
// If the assembly being requested matches a runtime assembly,
198-
// we'll use the runtime assembly instead of loading it in this context:
199-
if (TryLoadRuntimeAssembly(assemblyName, out assembly))
200+
// If this is a runtime restricted assembly, load it based on unification rules
201+
if (TryGetRuntimeAssembly(assemblyName, out ScriptRuntimeAssembly scriptRuntimeAssembly))
202+
{
203+
return LoadRuntimeAssembly(assemblyName, scriptRuntimeAssembly);
204+
}
205+
206+
// If the assembly being requested matches a host assembly, we'll use
207+
// the host assembly instead of loading it in this context:
208+
if (TryLoadHostEnvironmentAssembly(assemblyName, out assembly))
200209
{
201210
return assembly;
202211
}
203212

204-
if (TryGetRuntimeAssembly(assemblyName, out ScriptRuntimeAssembly scriptRuntimeAssembly))
213+
return LoadCore(assemblyName);
214+
}
215+
216+
private Assembly LoadRuntimeAssembly(AssemblyName assemblyName, ScriptRuntimeAssembly scriptRuntimeAssembly)
217+
{
218+
// If this is a private runtime assembly, return function dependency
219+
if (string.Equals(scriptRuntimeAssembly.ResolutionPolicy, PrivateDependencyResolutionPolicy))
205220
{
206-
// If there was a failure loading a runtime assembly, ensure we gracefuly unify to
207-
// the runtime version of the assembly if the version falls within a safe range.
208-
if (TryLoadRuntimeAssembly(new AssemblyName(assemblyName.Name), out assembly))
209-
{
210-
var policyEvaluator = GetResolutionPolicyEvaluator(scriptRuntimeAssembly.ResolutionPolicy);
221+
return LoadCore(assemblyName);
222+
}
211223

212-
if (policyEvaluator.Invoke(assemblyName, assembly))
213-
{
214-
return assembly;
215-
}
216-
}
224+
// Attempt to load the runtime version of the assembly based on the unification policy evaluation result.
225+
if (TryLoadHostEnvironmentAssembly(assemblyName, allowPartialNameMatch: true, out Assembly assembly))
226+
{
227+
var policyEvaluator = GetResolutionPolicyEvaluator(scriptRuntimeAssembly.ResolutionPolicy);
217228

218-
return null;
229+
if (policyEvaluator.Invoke(assemblyName, assembly))
230+
{
231+
return assembly;
232+
}
219233
}
220234

221-
return LoadCore(assemblyName);
235+
return null;
222236
}
223237

224238
private bool TryLoadDepsDependency(AssemblyName assemblyName, out Assembly assembly)
@@ -279,7 +293,13 @@ internal static bool TryGetDepsAsset(IDictionary<string, RuntimeAsset[]> depsAss
279293
return assemblyPath != null;
280294
}
281295

282-
private bool TryLoadRuntimeAssembly(AssemblyName assemblyName, out Assembly assembly)
296+
private bool TryLoadHostEnvironmentAssembly(AssemblyName assemblyName, bool allowPartialNameMatch, out Assembly assembly)
297+
{
298+
return TryLoadHostEnvironmentAssembly(assemblyName, out assembly)
299+
|| (allowPartialNameMatch && TryLoadHostEnvironmentAssembly(new AssemblyName(assemblyName.Name), out assembly));
300+
}
301+
302+
private bool TryLoadHostEnvironmentAssembly(AssemblyName assemblyName, out Assembly assembly)
283303
{
284304
assembly = null;
285305
try

src/WebJobs.Script/runtimeassemblies.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22
// This list originally comes from dumping TPA assemblies.
33
{
44
"runtimeAssemblies": [
5+
{
6+
"name": "DotNetTI.BreakingChangeAnalysis",
7+
"resolutionPolicy": "private"
8+
},
9+
{
10+
"name": "Marklio.Metadata",
11+
"resolutionPolicy": "private"
12+
},
513
{
614
"name": "Microsoft.ApplicationInsights",
715
"resolutionPolicy": "runtimeVersion"
@@ -738,6 +746,10 @@
738746
"name": "NuGet.Versioning",
739747
"resolutionPolicy": "minorMatchOrLower"
740748
},
749+
{
750+
"name": "protobuf-net",
751+
"resolutionPolicy": "private"
752+
},
741753
{
742754
"name": "SOS.NETCore",
743755
"resolutionPolicy": "minorMatchOrLower"

test/WebJobs.Script.Tests/Description/DotNet/FunctionAssemblyLoadContextTests.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,16 @@ namespace Microsoft.Azure.WebJobs.Script.Tests
2020
public class FunctionAssemblyLoadContextTests
2121
{
2222
[Theory]
23-
[InlineData("Microsoft.Azure.WebJobs")]
24-
[InlineData("Microsoft.Azure.WebJobs.Extensions.Http")]
25-
[InlineData("Microsoft.Azure.WebJobs.Host")]
26-
[InlineData("Microsoft.Azure.WebJobs.Logging")]
27-
[InlineData("Microsoft.Azure.WebJobs.Logging.ApplicationInsights")]
28-
[InlineData("Microsoft.Azure.WebJobs.Script")]
29-
[InlineData("Microsoft.Azure.WebJobs.Script.Grpc")]
30-
[InlineData("Microsoft.Azure.WebJobs.Script.WebHost")]
31-
[InlineData("Microsoft.Azure.WebSites.DataProtection")]
32-
[InlineData("System.IO")] // System.*
23+
[InlineData("Microsoft.Azure.WebJobs, Version=3.0.0.0")]
24+
[InlineData("Microsoft.Azure.WebJobs.Extensions.Http, Version=3.0.0.0")]
25+
[InlineData("Microsoft.Azure.WebJobs.Host, Version=3.0.0.0")]
26+
[InlineData("Microsoft.Azure.WebJobs.Logging, Version=3.0.0.0")]
27+
[InlineData("Microsoft.Azure.WebJobs.Logging.ApplicationInsights, Version=3.0.0.0")]
28+
[InlineData("Microsoft.Azure.WebJobs.Script, Version=3.0.0.0")]
29+
[InlineData("Microsoft.Azure.WebJobs.Script.Grpc, Version=3.0.0.0")]
30+
[InlineData("Microsoft.Azure.WebJobs.Script.WebHost, Version=3.0.0.0")]
31+
[InlineData("Microsoft.Azure.WebSites.DataProtection, Version=0.0.0.0")]
32+
[InlineData("System.IO, Version=0.0.0.0")] // System.*
3333
public void RuntimeAssemblies_AreLoadedInDefaultContext(string assemblyName)
3434
{
3535
var functionContext = new FunctionAssemblyLoadContext(AppContext.BaseDirectory);

0 commit comments

Comments
 (0)