Skip to content

Commit e3d15b8

Browse files
authored
Fix a potential out-of-bound read in RuntimeInvokeHostAssemblyResolver (#104536)
* Add asserts * Add test * Change return types of reverse FCalls that return Assembly to RuntimeAssembly to ensure type safety Fixes #104466 * Fix test * CR feedback: - Improve tests - Improve error messages
1 parent 11ca923 commit e3d15b8

File tree

11 files changed

+103
-89
lines changed

11 files changed

+103
-89
lines changed

src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -593,12 +593,12 @@ private CultureInfo GetLocale()
593593
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AssemblyNative_GetSimpleName")]
594594
private static partial void GetSimpleName(QCallAssembly assembly, StringHandleOnStack retSimpleName);
595595

596-
internal string? GetSimpleName()
596+
internal string GetSimpleName()
597597
{
598598
RuntimeAssembly runtimeAssembly = this;
599599
string? name = null;
600600
GetSimpleName(new QCallAssembly(ref runtimeAssembly), new StringHandleOnStack(ref name));
601-
return name;
601+
return name!;
602602
}
603603

604604
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AssemblyNative_GetHashAlgorithm")]

src/coreclr/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreCLR.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ internal Assembly LoadFromInMemoryModule(IntPtr moduleHandle)
110110

111111
// This method is invoked by the VM to resolve a satellite assembly reference
112112
// after trying assembly resolution via Load override without success.
113-
private static Assembly? ResolveSatelliteAssembly(IntPtr gchManagedAssemblyLoadContext, AssemblyName assemblyName)
113+
private static RuntimeAssembly? ResolveSatelliteAssembly(IntPtr gchManagedAssemblyLoadContext, AssemblyName assemblyName)
114114
{
115115
AssemblyLoadContext context = (AssemblyLoadContext)(GCHandle.FromIntPtr(gchManagedAssemblyLoadContext).Target)!;
116116

@@ -136,7 +136,7 @@ private static IntPtr ResolveUnmanagedDllUsingEvent(string unmanagedDllName, Ass
136136

137137
// This method is invoked by the VM to resolve an assembly reference using the Resolving event
138138
// after trying assembly resolution via Load override and TPA load context without success.
139-
private static Assembly? ResolveUsingResolvingEvent(IntPtr gchManagedAssemblyLoadContext, AssemblyName assemblyName)
139+
private static RuntimeAssembly? ResolveUsingResolvingEvent(IntPtr gchManagedAssemblyLoadContext, AssemblyName assemblyName)
140140
{
141141
AssemblyLoadContext context = (AssemblyLoadContext)(GCHandle.FromIntPtr(gchManagedAssemblyLoadContext).Target)!;
142142
// Invoke the AssemblyResolve event callbacks if wired up

src/coreclr/dlls/mscorrc/mscorrc.rc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -702,7 +702,7 @@ END
702702
STRINGTABLE DISCARDABLE
703703
BEGIN
704704
IDS_HOST_ASSEMBLY_RESOLVER_ASSEMBLY_ALREADY_LOADED_IN_CONTEXT "Assembly with same name is already loaded"
705-
IDS_HOST_ASSEMBLY_RESOLVER_DYNAMICALLY_EMITTED_ASSEMBLIES_UNSUPPORTED "Dynamically emitted assemblies are unsupported during host-based resolution."
705+
IDS_HOST_ASSEMBLY_RESOLVER_DYNAMICALLY_EMITTED_ASSEMBLIES_UNSUPPORTED "Dynamically emitted assemblies are unsupported for AssemblyLoadContext resolution."
706706
END
707707

708708
STRINGTABLE DISCARDABLE

src/coreclr/vm/appdomain.cpp

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3965,7 +3965,7 @@ Assembly* AppDomain::RaiseTypeResolveEventThrowing(Assembly* pAssembly, LPCSTR s
39653965
GCX_COOP();
39663966

39673967
struct {
3968-
OBJECTREF AssemblyRef;
3968+
ASSEMBLYREF AssemblyRef;
39693969
STRINGREF str;
39703970
} gc;
39713971
gc.AssemblyRef = NULL;
@@ -3974,7 +3974,7 @@ Assembly* AppDomain::RaiseTypeResolveEventThrowing(Assembly* pAssembly, LPCSTR s
39743974
GCPROTECT_BEGIN(gc);
39753975

39763976
if (pAssembly != NULL)
3977-
gc.AssemblyRef = pAssembly->GetExposedObject();
3977+
gc.AssemblyRef = (ASSEMBLYREF)pAssembly->GetExposedObject();
39783978

39793979
MethodDescCallSite onTypeResolve(METHOD__ASSEMBLYLOADCONTEXT__ON_TYPE_RESOLVE);
39803980

@@ -3984,14 +3984,16 @@ Assembly* AppDomain::RaiseTypeResolveEventThrowing(Assembly* pAssembly, LPCSTR s
39843984
ObjToArgSlot(gc.AssemblyRef),
39853985
ObjToArgSlot(gc.str)
39863986
};
3987-
ASSEMBLYREF ResultingAssemblyRef = (ASSEMBLYREF) onTypeResolve.Call_RetOBJECTREF(args);
3987+
gc.AssemblyRef = (ASSEMBLYREF) onTypeResolve.Call_RetOBJECTREF(args);
39883988

3989-
if (ResultingAssemblyRef != NULL)
3989+
if (gc.AssemblyRef != NULL)
39903990
{
3991-
pResolvedAssembly = ResultingAssemblyRef->GetAssembly();
3991+
_ASSERTE(CoreLibBinder::IsClass(gc.AssemblyRef->GetMethodTable(), CLASS__ASSEMBLY));
3992+
3993+
pResolvedAssembly = gc.AssemblyRef->GetAssembly();
39923994

39933995
if (pResultingAssemblyRef)
3994-
*pResultingAssemblyRef = ResultingAssemblyRef;
3996+
*pResultingAssemblyRef = gc.AssemblyRef;
39953997
else
39963998
{
39973999
if (pResolvedAssembly->IsCollectible())
@@ -4023,7 +4025,7 @@ Assembly* AppDomain::RaiseResourceResolveEvent(Assembly* pAssembly, LPCSTR szNam
40234025
GCX_COOP();
40244026

40254027
struct {
4026-
OBJECTREF AssemblyRef;
4028+
ASSEMBLYREF AssemblyRef;
40274029
STRINGREF str;
40284030
} gc;
40294031
gc.AssemblyRef = NULL;
@@ -4032,7 +4034,7 @@ Assembly* AppDomain::RaiseResourceResolveEvent(Assembly* pAssembly, LPCSTR szNam
40324034
GCPROTECT_BEGIN(gc);
40334035

40344036
if (pAssembly != NULL)
4035-
gc.AssemblyRef=pAssembly->GetExposedObject();
4037+
gc.AssemblyRef=(ASSEMBLYREF)pAssembly->GetExposedObject();
40364038

40374039
MethodDescCallSite onResourceResolve(METHOD__ASSEMBLYLOADCONTEXT__ON_RESOURCE_RESOLVE);
40384040
gc.str = StringObject::NewString(szName);
@@ -4041,10 +4043,12 @@ Assembly* AppDomain::RaiseResourceResolveEvent(Assembly* pAssembly, LPCSTR szNam
40414043
ObjToArgSlot(gc.AssemblyRef),
40424044
ObjToArgSlot(gc.str)
40434045
};
4044-
ASSEMBLYREF ResultingAssemblyRef = (ASSEMBLYREF) onResourceResolve.Call_RetOBJECTREF(args);
4045-
if (ResultingAssemblyRef != NULL)
4046+
gc.AssemblyRef = (ASSEMBLYREF) onResourceResolve.Call_RetOBJECTREF(args);
4047+
if (gc.AssemblyRef != NULL)
40464048
{
4047-
pResolvedAssembly = ResultingAssemblyRef->GetAssembly();
4049+
_ASSERTE(CoreLibBinder::IsClass(gc.AssemblyRef->GetMethodTable(), CLASS__ASSEMBLY));
4050+
4051+
pResolvedAssembly = gc.AssemblyRef->GetAssembly();
40484052
if (pResolvedAssembly->IsCollectible())
40494053
{
40504054
COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleAssemblyResolve"));
@@ -4085,7 +4089,7 @@ AppDomain::RaiseAssemblyResolveEvent(
40854089
Assembly* pAssembly = NULL;
40864090

40874091
struct {
4088-
OBJECTREF AssemblyRef;
4092+
ASSEMBLYREF AssemblyRef;
40894093
STRINGREF str;
40904094
} gc;
40914095
gc.AssemblyRef = NULL;
@@ -4095,7 +4099,7 @@ AppDomain::RaiseAssemblyResolveEvent(
40954099
{
40964100
if (pSpec->GetParentAssembly() != NULL)
40974101
{
4098-
gc.AssemblyRef=pSpec->GetParentAssembly()->GetExposedObject();
4102+
gc.AssemblyRef=(ASSEMBLYREF)pSpec->GetParentAssembly()->GetExposedObject();
40994103
}
41004104

41014105
MethodDescCallSite onAssemblyResolve(METHOD__ASSEMBLYLOADCONTEXT__ON_ASSEMBLY_RESOLVE);
@@ -4106,11 +4110,13 @@ AppDomain::RaiseAssemblyResolveEvent(
41064110
ObjToArgSlot(gc.str)
41074111
};
41084112

4109-
ASSEMBLYREF ResultingAssemblyRef = (ASSEMBLYREF) onAssemblyResolve.Call_RetOBJECTREF(args);
4113+
gc.AssemblyRef = (ASSEMBLYREF) onAssemblyResolve.Call_RetOBJECTREF(args);
41104114

4111-
if (ResultingAssemblyRef != NULL)
4115+
if (gc.AssemblyRef != NULL)
41124116
{
4113-
pAssembly = ResultingAssemblyRef->GetAssembly();
4117+
_ASSERTE(CoreLibBinder::IsClass(gc.AssemblyRef->GetMethodTable(), CLASS__ASSEMBLY));
4118+
4119+
pAssembly = gc.AssemblyRef->GetAssembly();
41144120
if (pAssembly->IsCollectible())
41154121
{
41164122
COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleAssemblyResolve"));
@@ -4543,6 +4549,8 @@ HRESULT RuntimeInvokeHostAssemblyResolver(INT_PTR pManagedAssemblyLoadContextToB
45434549
// If we are here, assembly was successfully resolved via Load or Resolving events.
45444550
_ASSERTE(_gcRefs.oRefLoadedAssembly != NULL);
45454551

4552+
_ASSERTE(CoreLibBinder::IsClass(_gcRefs.oRefLoadedAssembly->GetMethodTable(), CLASS__ASSEMBLY));
4553+
45464554
// We were able to get the assembly loaded. Now, get its name since the host could have
45474555
// performed the resolution using an assembly with different name.
45484556
DomainAssembly *pDomainAssembly = _gcRefs.oRefLoadedAssembly->GetDomainAssembly();

src/coreclr/vm/corelib.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -867,11 +867,11 @@ DEFINE_FIELD_U(_id, AssemblyLoadContextBaseObject, _id)
867867
DEFINE_FIELD_U(_state, AssemblyLoadContextBaseObject, _state)
868868
DEFINE_FIELD_U(_isCollectible, AssemblyLoadContextBaseObject, _isCollectible)
869869
DEFINE_CLASS(ASSEMBLYLOADCONTEXT, Loader, AssemblyLoadContext)
870-
DEFINE_METHOD(ASSEMBLYLOADCONTEXT, RESOLVE, Resolve, SM_IntPtr_AssemblyName_RetAssemblyBase)
870+
DEFINE_METHOD(ASSEMBLYLOADCONTEXT, RESOLVE, Resolve, SM_IntPtr_AssemblyName_RetAssembly)
871871
DEFINE_METHOD(ASSEMBLYLOADCONTEXT, RESOLVEUNMANAGEDDLL, ResolveUnmanagedDll, SM_Str_IntPtr_RetIntPtr)
872872
DEFINE_METHOD(ASSEMBLYLOADCONTEXT, RESOLVEUNMANAGEDDLLUSINGEVENT, ResolveUnmanagedDllUsingEvent, SM_Str_AssemblyBase_IntPtr_RetIntPtr)
873-
DEFINE_METHOD(ASSEMBLYLOADCONTEXT, RESOLVEUSINGEVENT, ResolveUsingResolvingEvent, SM_IntPtr_AssemblyName_RetAssemblyBase)
874-
DEFINE_METHOD(ASSEMBLYLOADCONTEXT, RESOLVESATELLITEASSEMBLY, ResolveSatelliteAssembly, SM_IntPtr_AssemblyName_RetAssemblyBase)
873+
DEFINE_METHOD(ASSEMBLYLOADCONTEXT, RESOLVEUSINGEVENT, ResolveUsingResolvingEvent, SM_IntPtr_AssemblyName_RetAssembly)
874+
DEFINE_METHOD(ASSEMBLYLOADCONTEXT, RESOLVESATELLITEASSEMBLY, ResolveSatelliteAssembly, SM_IntPtr_AssemblyName_RetAssembly)
875875
DEFINE_FIELD(ASSEMBLYLOADCONTEXT, ASSEMBLY_LOAD, AssemblyLoad)
876876
DEFINE_METHOD(ASSEMBLYLOADCONTEXT, ON_ASSEMBLY_LOAD, OnAssemblyLoad, SM_Assembly_RetVoid)
877877
DEFINE_METHOD(ASSEMBLYLOADCONTEXT, ON_RESOURCE_RESOLVE, OnResourceResolve, SM_Assembly_Str_RetAssembly)

src/coreclr/vm/metasig.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -393,8 +393,6 @@ DEFINE_METASIG(IM(IntPtr_Int_RetVoid, I i, v))
393393
DEFINE_METASIG(IM(IntInt_RetArrByte, i i, a(b)))
394394
DEFINE_METASIG(IM(RetIntPtr, _, I))
395395
DEFINE_METASIG(IM(RetInt, _, i))
396-
DEFINE_METASIG_T(IM(RetAssemblyName, _, C(ASSEMBLY_NAME)))
397-
DEFINE_METASIG_T(IM(RetAssemblyBase, _, C(ASSEMBLYBASE)))
398396
DEFINE_METASIG_T(IM(RetModule, _, C(MODULE)))
399397
DEFINE_METASIG_T(IM(PtrNativeAssemblyNameParts, P(g(NATIVE_ASSEMBLY_NAME_PARTS)), v))
400398
DEFINE_METASIG(SM(PtrCharPtrVoid, P(u) P(v), v))
@@ -456,8 +454,6 @@ DEFINE_METASIG(IM(Int_Int_Int_Int_RetVoid, i i i i, v))
456454
DEFINE_METASIG_T(IM(Obj_EventArgs_RetVoid, j C(EVENT_ARGS), v))
457455
DEFINE_METASIG_T(IM(Obj_UnhandledExceptionEventArgs_RetVoid, j C(UNHANDLED_EVENTARGS), v))
458456

459-
DEFINE_METASIG_T(IM(Assembly_RetBool, C(ASSEMBLY), F))
460-
DEFINE_METASIG_T(IM(AssemblyBase_RetBool, C(ASSEMBLYBASE), F))
461457
DEFINE_METASIG_T(IM(Exception_RetVoid, C(EXCEPTION), v))
462458

463459
DEFINE_METASIG(IM(IntPtr_RetObj, I, j))
@@ -565,7 +561,7 @@ DEFINE_METASIG_T(IM(RefGuid_OutIntPtr_RetCustomQueryInterfaceResult, r(g(GUID))
565561
DEFINE_METASIG_T(SM(RefGuid_RefGuid_RetVoid, r(g(GUID)) r(g(GUID)) , v))
566562
DEFINE_METASIG_T(SM(RefGuid_RetVoid, r(g(GUID)), v))
567563

568-
DEFINE_METASIG_T(SM(IntPtr_AssemblyName_RetAssemblyBase, I C(ASSEMBLY_NAME), C(ASSEMBLYBASE)))
564+
DEFINE_METASIG_T(SM(IntPtr_AssemblyName_RetAssembly, I C(ASSEMBLY_NAME), C(ASSEMBLY)))
569565
DEFINE_METASIG_T(SM(Str_AssemblyBase_IntPtr_RetIntPtr, s C(ASSEMBLYBASE) I, I))
570566
DEFINE_METASIG_T(SM(Str_AssemblyBase_Bool_UInt_RetIntPtr, s C(ASSEMBLYBASE) F K, I))
571567

src/libraries/System.Private.CoreLib/src/Resources/Strings.resx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,8 +1011,11 @@
10111011
<data name="Argument_CultureNotSupportedInInvariantMode" xml:space="preserve">
10121012
<value>Only the invariant culture is supported in globalization-invariant mode. See https://aka.ms/GlobalizationInvariantMode for more information.</value>
10131013
</data>
1014-
<data name="Argument_CustomAssemblyLoadContextRequestedNameMismatch" xml:space="preserve">
1015-
<value>Resolved assembly's simple name should be the same as of the requested assembly.</value>
1014+
<data name="InvalidOperation_ResolvedAssemblyMustBeRuntimeAssembly" xml:space="preserve">
1015+
<value>Resolved assembly must be a runtime Assembly object.</value>
1016+
</data>
1017+
<data name="InvalidOperation_ResolvedAssemblyRequestedNameMismatch" xml:space="preserve">
1018+
<value>Resolved assembly's simple name must be the same as of the requested assembly.</value>
10161019
</data>
10171020
<data name="Argument_CustomCultureCannotBePassedByNumber" xml:space="preserve">
10181021
<value>Customized cultures cannot be passed by LCID, only by name.</value>

src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,7 @@ public void Dispose()
604604
#if !NATIVEAOT
605605
// This method is invoked by the VM when using the host-provided assembly load context
606606
// implementation.
607-
private static Assembly? Resolve(IntPtr gchManagedAssemblyLoadContext, AssemblyName assemblyName)
607+
private static RuntimeAssembly? Resolve(IntPtr gchManagedAssemblyLoadContext, AssemblyName assemblyName)
608608
{
609609
AssemblyLoadContext context = (AssemblyLoadContext)(GCHandle.FromIntPtr(gchManagedAssemblyLoadContext).Target)!;
610610

@@ -639,56 +639,44 @@ public void Dispose()
639639
return null;
640640
}
641641

642-
private static Assembly ValidateAssemblyNameWithSimpleName(Assembly assembly, string? requestedSimpleName)
642+
private static RuntimeAssembly ValidateAssemblyNameWithSimpleName(Assembly assembly, string? requestedSimpleName)
643643
{
644644
ArgumentException.ThrowIfNullOrEmpty(requestedSimpleName, "AssemblyName.Name");
645645

646-
// Get the name of the loaded assembly
647-
string? loadedSimpleName = null;
648-
649646
// Derived type's Load implementation is expected to use one of the LoadFrom* methods to get the assembly
650647
// which is a RuntimeAssembly instance. However, since Assembly type can be used build any other artifact (e.g. AssemblyBuilder),
651648
// we need to check for RuntimeAssembly.
652-
RuntimeAssembly? rtLoadedAssembly = GetRuntimeAssembly(assembly);
653-
if (rtLoadedAssembly != null)
649+
RuntimeAssembly? runtimeAssembly = GetRuntimeAssembly(assembly);
650+
if (runtimeAssembly == null)
654651
{
655-
loadedSimpleName = rtLoadedAssembly.GetSimpleName();
652+
throw new InvalidOperationException(SR.InvalidOperation_ResolvedAssemblyMustBeRuntimeAssembly);
656653
}
657654

658-
// The simple names should match at the very least
659-
if (string.IsNullOrEmpty(loadedSimpleName) || !requestedSimpleName.Equals(loadedSimpleName, StringComparison.InvariantCultureIgnoreCase))
655+
if (!requestedSimpleName.Equals(runtimeAssembly.GetSimpleName(), StringComparison.InvariantCultureIgnoreCase))
660656
{
661-
throw new InvalidOperationException(SR.Argument_CustomAssemblyLoadContextRequestedNameMismatch);
657+
throw new InvalidOperationException(SR.InvalidOperation_ResolvedAssemblyRequestedNameMismatch);
662658
}
663659

664-
return assembly;
660+
return runtimeAssembly;
665661
}
666662

667-
private Assembly? ResolveUsingLoad(AssemblyName assemblyName)
663+
private RuntimeAssembly? ResolveUsingLoad(AssemblyName assemblyName)
668664
{
669665
string? simpleName = assemblyName.Name;
670-
Assembly? assembly = Load(assemblyName);
671666

672-
if (assembly != null)
673-
{
674-
assembly = ValidateAssemblyNameWithSimpleName(assembly, simpleName);
675-
}
667+
Assembly? assembly = Load(assemblyName);
676668

677-
return assembly;
669+
return (assembly != null) ? ValidateAssemblyNameWithSimpleName(assembly, simpleName) : null;
678670
}
679671

680-
private Assembly? ResolveUsingEvent(AssemblyName assemblyName)
672+
private RuntimeAssembly? ResolveUsingEvent(AssemblyName assemblyName)
681673
{
682674
string? simpleName = assemblyName.Name;
683675

684676
// Invoke the Resolving event callbacks if wired up
685677
Assembly? assembly = GetFirstResolvedAssemblyFromResolvingEvent(assemblyName);
686-
if (assembly != null)
687-
{
688-
assembly = ValidateAssemblyNameWithSimpleName(assembly, simpleName);
689-
}
690678

691-
return assembly;
679+
return (assembly != null) ? ValidateAssemblyNameWithSimpleName(assembly, simpleName) : null;
692680
}
693681

694682
// This method is called by the VM.
@@ -755,7 +743,7 @@ internal static void InvokeAssemblyLoadEvent(Assembly assembly)
755743
Justification = "Satellite assemblies have no code in them and loading is not a problem")]
756744
[UnconditionalSuppressMessage("SingleFile", "IL3000: Avoid accessing Assembly file path when publishing as a single file",
757745
Justification = "This call is fine because native call runs before this and checks BindSatelliteResourceFromBundle")]
758-
private Assembly? ResolveSatelliteAssembly(AssemblyName assemblyName)
746+
private RuntimeAssembly? ResolveSatelliteAssembly(AssemblyName assemblyName)
759747
{
760748
// Called by native runtime when CultureName is not empty
761749
Debug.Assert(assemblyName.CultureName?.Length > 0);
@@ -767,7 +755,7 @@ internal static void InvokeAssemblyLoadEvent(Assembly assembly)
767755

768756
string parentAssemblyName = assemblyName.Name.Substring(0, assemblyName.Name.Length - SatelliteSuffix.Length);
769757

770-
Assembly parentAssembly = LoadFromAssemblyName(new AssemblyName(parentAssemblyName));
758+
RuntimeAssembly parentAssembly = (RuntimeAssembly)LoadFromAssemblyName(new AssemblyName(parentAssemblyName));
771759

772760
AssemblyLoadContext parentALC = GetLoadContext(parentAssembly)!;
773761

@@ -790,7 +778,7 @@ internal static void InvokeAssemblyLoadEvent(Assembly assembly)
790778
exists = FileSystem.FileExists(assemblyPath);
791779
}
792780

793-
Assembly? asm = exists ? parentALC.LoadFromAssemblyPath(assemblyPath) : null;
781+
RuntimeAssembly? asm = exists ? (RuntimeAssembly?)parentALC.LoadFromAssemblyPath(assemblyPath) : null;
794782
#if CORECLR
795783
if (IsTracingEnabled())
796784
{

src/libraries/System.Runtime.Loader/tests/AssemblyLoadContextTest.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.IO;
88
using System.Linq;
99
using System.Reflection;
10+
using System.Reflection.Emit;
1011
using System.Threading.Tasks;
1112

1213
namespace System.Runtime.Loader.Tests
@@ -233,5 +234,50 @@ public static void SubclassAssemblyLoadContext_Properties()
233234
Assert.Contains(alc, AssemblyLoadContext.All);
234235
Assert.Empty(alc.Assemblies);
235236
}
237+
238+
class RefEmitLoadContext : AssemblyLoadContext
239+
{
240+
protected override Assembly? Load(AssemblyName assemblyName)
241+
{
242+
return AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
243+
}
244+
}
245+
246+
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))]
247+
[ActiveIssue("https://github.com/dotnet/runtime/issues/31804", TestRuntimes.Mono)]
248+
public static void LoadRefEmitAssembly()
249+
{
250+
RefEmitLoadContext alc = new();
251+
alc.Resolving += (sender, assembly) => { Assert.Fail("Resolving event not expected"); return null; };
252+
Exception error = Assert.Throws<FileLoadException>(() => alc.LoadFromAssemblyName(new AssemblyName("MyAssembly")));
253+
Assert.IsType<InvalidOperationException>(error.InnerException);
254+
}
255+
256+
class NonRuntimeAssemblyContext : AssemblyLoadContext
257+
{
258+
class NonRuntimeAssembly : Assembly
259+
{
260+
private AssemblyName _name;
261+
262+
public NonRuntimeAssembly(AssemblyName name) => _name = name;
263+
264+
public override AssemblyName GetName(bool copiedName) => _name;
265+
}
266+
267+
protected override Assembly? Load(AssemblyName assemblyName)
268+
{
269+
return new NonRuntimeAssembly(assemblyName);
270+
}
271+
}
272+
273+
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsAssemblyLoadingSupported))]
274+
[ActiveIssue("https://github.com/dotnet/runtime/issues/31804", TestRuntimes.Mono)]
275+
public static void LoadNonRuntimeAssembly()
276+
{
277+
NonRuntimeAssemblyContext alc = new();
278+
alc.Resolving += (sender, assembly) => { Assert.Fail("Resolving event not expected"); return null; };
279+
Exception error = Assert.Throws<FileLoadException>(() => alc.LoadFromAssemblyName(new AssemblyName("MyAssembly")));
280+
Assert.IsType<InvalidOperationException>(error.InnerException);
281+
}
236282
}
237283
}

0 commit comments

Comments
 (0)