Skip to content

Commit 2154551

Browse files
authored
[release/6.0] Unloadability bugs fixes (#68674)
* [Release/6.0] Port unloadability fixes This change ports two unloadability fixes for issues found by a customer. * #68550 that adds reference between native `LoaderAllocator`s of two collectible `AssemblyLoadContexts` when one of them is used to resolve assembly using the other one. This reference ensures that the one that provides the resolved `Assembly` isn't collected before the one that uses it. * #68336 that adds a missing lock around `m_LoaderAllocatorReferences` iteration during assembly load context destruction. * Remove breaking change part The change in main has added an exception in case a collectible `Assembly` is resolved into a non-collectible `AssemblyLoadContext`. Although that is incorrect, there are cases when it would work. So the added exception is a breaking change that I think we likely don't want to get into 6.0
1 parent e41228d commit 2154551

File tree

6 files changed

+37
-17
lines changed

6 files changed

+37
-17
lines changed

src/coreclr/binder/assemblybinder.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
extern HRESULT RuntimeInvokeHostAssemblyResolver(INT_PTR pManagedAssemblyLoadContextToBindWithin,
3434
BINDER_SPACE::AssemblyName *pAssemblyName,
3535
CLRPrivBinderCoreCLR *pTPABinder,
36+
AssemblyLoadContext *pBinder,
3637
ICLRPrivAssembly **ppLoadedAssembly);
3738

3839
#endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
@@ -1408,6 +1409,7 @@ namespace BINDER_SPACE
14081409
HRESULT AssemblyBinder::BindUsingHostAssemblyResolver(/* in */ INT_PTR pManagedAssemblyLoadContextToBindWithin,
14091410
/* in */ AssemblyName *pAssemblyName,
14101411
/* in */ CLRPrivBinderCoreCLR *pTPABinder,
1412+
/* in */ AssemblyLoadContext *pBinder,
14111413
/* out */ Assembly **ppAssembly)
14121414
{
14131415
HRESULT hr = E_FAIL;
@@ -1417,7 +1419,7 @@ HRESULT AssemblyBinder::BindUsingHostAssemblyResolver(/* in */ INT_PTR pManagedA
14171419
// RuntimeInvokeHostAssemblyResolver will perform steps 2-4 of CLRPrivBinderAssemblyLoadContext::BindAssemblyByName.
14181420
ICLRPrivAssembly *pLoadedAssembly = NULL;
14191421
hr = RuntimeInvokeHostAssemblyResolver(pManagedAssemblyLoadContextToBindWithin,
1420-
pAssemblyName, pTPABinder, &pLoadedAssembly);
1422+
pAssemblyName, pTPABinder, pBinder, &pLoadedAssembly);
14211423
if (SUCCEEDED(hr))
14221424
{
14231425
_ASSERTE(pLoadedAssembly != NULL);

src/coreclr/binder/clrprivbinderassemblyloadcontext.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ HRESULT CLRPrivBinderAssemblyLoadContext::BindAssemblyByName(AssemblyNameData *p
8484
// of what to do next. The host-overridden binder can either fail the bind or return reference to an existing assembly
8585
// that has been loaded.
8686
//
87-
hr = AssemblyBinder::BindUsingHostAssemblyResolver(GetManagedAssemblyLoadContext(), pAssemblyName, m_pTPABinder, &pCoreCLRFoundAssembly);
87+
hr = AssemblyBinder::BindUsingHostAssemblyResolver(GetManagedAssemblyLoadContext(), pAssemblyName, m_pTPABinder, this, &pCoreCLRFoundAssembly);
8888
if (SUCCEEDED(hr))
8989
{
9090
// We maybe returned an assembly that was bound to a different AssemblyLoadContext instance.
@@ -258,11 +258,13 @@ void CLRPrivBinderAssemblyLoadContext::PrepareForLoadContextRelease(INT_PTR ptrM
258258
_ASSERTE(m_pAssemblyLoaderAllocator != NULL);
259259
_ASSERTE(m_loaderAllocatorHandle != NULL);
260260

261-
// We cannot delete the binder here as it is used indirectly when comparing assemblies with the same binder
262-
// It will be deleted when the LoaderAllocator will be deleted
263-
// But we can release the LoaderAllocator as we are no longer using it here
261+
// We need to keep the LoaderAllocator pointer set as it still may be needed for creating references between the
262+
// native LoaderAllocators of two collectible contexts in case the AssemblyLoadContext.Unload was called on the current
263+
// context before returning from its AssemblyLoadContext.Load override or the context's Resolving event.
264+
// But we need to release the LoaderAllocator so that it doesn't prevent completion of the final phase of unloading in
265+
// some cases. It is safe to do as the AssemblyLoaderAllocator is guaranteed to be alive at least until the
266+
// CustomAssemblyBinder::ReleaseLoadContext is called, where we NULL this pointer.
264267
m_pAssemblyLoaderAllocator->Release();
265-
m_pAssemblyLoaderAllocator = NULL;
266268

267269
// Destroy the strong handle to the LoaderAllocator in order to let it reach its finalizer
268270
DestroyHandle(reinterpret_cast<OBJECTHANDLE>(m_loaderAllocatorHandle));
@@ -287,6 +289,10 @@ void CLRPrivBinderAssemblyLoadContext::ReleaseLoadContext()
287289
handle = reinterpret_cast<OBJECTHANDLE>(m_ptrManagedStrongAssemblyLoadContext);
288290
DestroyHandle(handle);
289291
m_ptrManagedAssemblyLoadContext = NULL;
292+
293+
// The AssemblyLoaderAllocator is in a process of shutdown and should not be used
294+
// after this point.
295+
m_pAssemblyLoaderAllocator = NULL;
290296
}
291297

292298
#endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)

src/coreclr/binder/clrprivbindercoreclr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ HRESULT CLRPrivBinderCoreCLR::BindUsingAssemblyName(BINDER_SPACE::AssemblyName *
107107
if (pManagedAssemblyLoadContext != NULL)
108108
{
109109
hr = AssemblyBinder::BindUsingHostAssemblyResolver(pManagedAssemblyLoadContext, pAssemblyName,
110-
NULL, &pCoreCLRFoundAssembly);
110+
NULL, this, &pCoreCLRFoundAssembly);
111111
if (SUCCEEDED(hr))
112112
{
113113
// We maybe returned an assembly that was bound to a different AssemblyLoadContext instance.

src/coreclr/binder/inc/assemblybinder.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
class CLRPrivBinderCoreCLR;
2222
class PEAssembly;
2323
class PEImage;
24+
class AssemblyLoadContext;
2425

2526
namespace BINDER_SPACE
2627
{
@@ -59,6 +60,7 @@ namespace BINDER_SPACE
5960
static HRESULT BindUsingHostAssemblyResolver (/* in */ INT_PTR pManagedAssemblyLoadContextToBindWithin,
6061
/* in */ AssemblyName *pAssemblyName,
6162
/* in */ CLRPrivBinderCoreCLR *pTPABinder,
63+
/* in */ AssemblyLoadContext *pBinder,
6264
/* out */ Assembly **ppAssembly);
6365

6466
static HRESULT BindUsingPEImage(/* in */ ApplicationContext *pApplicationContext,

src/coreclr/vm/appdomain.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5275,7 +5275,7 @@ AppDomain::AssemblyIterator::Next_Unlocked(
52755275
#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
52765276

52775277
// Returns S_OK if the assembly was successfully loaded
5278-
HRESULT RuntimeInvokeHostAssemblyResolver(INT_PTR pManagedAssemblyLoadContextToBindWithin, BINDER_SPACE::AssemblyName *pAssemblyName, CLRPrivBinderCoreCLR *pTPABinder, ICLRPrivAssembly **ppLoadedAssembly)
5278+
HRESULT RuntimeInvokeHostAssemblyResolver(INT_PTR pManagedAssemblyLoadContextToBindWithin, BINDER_SPACE::AssemblyName *pAssemblyName, CLRPrivBinderCoreCLR *pTPABinder, AssemblyLoadContext *pBinder, ICLRPrivAssembly **ppLoadedAssembly)
52795279
{
52805280
CONTRACTL
52815281
{
@@ -5453,6 +5453,21 @@ HRESULT RuntimeInvokeHostAssemblyResolver(INT_PTR pManagedAssemblyLoadContextToB
54535453
COMPlusThrowHR(COR_E_INVALIDOPERATION, IDS_HOST_ASSEMBLY_RESOLVER_DYNAMICALLY_EMITTED_ASSEMBLIES_UNSUPPORTED, name);
54545454
}
54555455

5456+
// For collectible assemblies, ensure that the parent loader allocator keeps the assembly's loader allocator
5457+
// alive for all its lifetime.
5458+
if (pDomainAssembly->IsCollectible())
5459+
{
5460+
LoaderAllocator *pResultAssemblyLoaderAllocator = pDomainAssembly->GetLoaderAllocator();
5461+
LoaderAllocator *pParentLoaderAllocator = NULL;
5462+
hr = pBinder->GetLoaderAllocator((LPVOID*)&pParentLoaderAllocator);
5463+
if (SUCCEEDED(hr))
5464+
{
5465+
_ASSERTE(pParentLoaderAllocator);
5466+
_ASSERTE(pResultAssemblyLoaderAllocator);
5467+
pParentLoaderAllocator->EnsureReference(pResultAssemblyLoaderAllocator);
5468+
}
5469+
}
5470+
54565471
pResolvedAssembly = pLoadedPEAssembly->GetHostAssembly();
54575472
}
54585473

src/coreclr/vm/loaderallocator.cpp

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -401,8 +401,9 @@ LoaderAllocator * LoaderAllocator::GCLoaderAllocators_RemoveAssemblies(AppDomain
401401
#endif //0
402402

403403
AppDomain::AssemblyIterator i;
404-
// Iterate through every loader allocator, marking as we go
405404
{
405+
// Iterate through every loader allocator, marking as we go
406+
CrstHolder chLoaderAllocatorReferencesLock(pAppDomain->GetLoaderAllocatorReferencesLock());
406407
CrstHolder chAssemblyListLock(pAppDomain->GetAssemblyListLock());
407408

408409
i = pAppDomain->IterateAssembliesEx((AssemblyIterationFlags)(
@@ -424,17 +425,11 @@ LoaderAllocator * LoaderAllocator::GCLoaderAllocators_RemoveAssemblies(AppDomain
424425
}
425426
}
426427
}
427-
}
428-
429-
// Iterate through every loader allocator, unmarking marked loaderallocators, and
430-
// build a free list of unmarked ones
431-
{
432-
CrstHolder chLoaderAllocatorReferencesLock(pAppDomain->GetLoaderAllocatorReferencesLock());
433-
CrstHolder chAssemblyListLock(pAppDomain->GetAssemblyListLock());
434428

429+
// Iterate through every loader allocator, unmarking marked loaderallocators, and
430+
// build a free list of unmarked ones
435431
i = pAppDomain->IterateAssembliesEx((AssemblyIterationFlags)(
436432
kIncludeExecution | kIncludeLoaded | kIncludeCollected));
437-
CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
438433

439434
while (i.Next_Unlocked(pDomainAssembly.This()))
440435
{

0 commit comments

Comments
 (0)