Skip to content

Commit 29b1e99

Browse files
Distinguish OOM and failure to reprotect in thunks APIs (#116010)
Resolves #113114.
1 parent 693d3bb commit 29b1e99

File tree

8 files changed

+98
-133
lines changed

8 files changed

+98
-133
lines changed

src/coreclr/nativeaot/Runtime/PalRedhawk.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@
6868
// we have to (in which case these definitions will move to CommonTypes.h).
6969
typedef int32_t HRESULT;
7070

71+
#define S_OK 0x0
72+
#define E_FAIL 0x80004005
73+
#define E_OUTOFMEMORY 0x8007000E
74+
7175
typedef WCHAR * LPWSTR;
7276
typedef const WCHAR * LPCWSTR;
7377
typedef char * LPSTR;

src/coreclr/nativeaot/Runtime/ThunksMapping.cpp

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,15 @@ FCIMPL0(int, RhpGetThunkBlockSize)
100100
}
101101
FCIMPLEND
102102

103-
EXTERN_C void* QCALLTYPE RhAllocateThunksMapping()
103+
EXTERN_C HRESULT QCALLTYPE RhAllocateThunksMapping(void** ppThunksSection)
104104
{
105105
#ifdef WIN32
106106

107107
void * pNewMapping = PalVirtualAlloc(THUNKS_MAP_SIZE * 2, PAGE_READWRITE);
108108
if (pNewMapping == NULL)
109-
return NULL;
109+
{
110+
return E_OUTOFMEMORY;
111+
}
110112

111113
void * pThunksSection = pNewMapping;
112114
void * pDataSection = (uint8_t*)pNewMapping + THUNKS_MAP_SIZE;
@@ -120,7 +122,9 @@ EXTERN_C void* QCALLTYPE RhAllocateThunksMapping()
120122
// changed anymore.
121123
void * pNewMapping = PalVirtualAlloc(THUNKS_MAP_SIZE * 2, PAGE_EXECUTE_READ);
122124
if (pNewMapping == NULL)
123-
return NULL;
125+
{
126+
return E_OUTOFMEMORY;
127+
}
124128

125129
void * pThunksSection = pNewMapping;
126130
void * pDataSection = (uint8_t*)pNewMapping + THUNKS_MAP_SIZE;
@@ -129,7 +133,7 @@ EXTERN_C void* QCALLTYPE RhAllocateThunksMapping()
129133
!PalVirtualProtect(pThunksSection, THUNKS_MAP_SIZE, PAGE_EXECUTE_READWRITE))
130134
{
131135
PalVirtualFree(pNewMapping, THUNKS_MAP_SIZE * 2);
132-
return NULL;
136+
return E_FAIL;
133137
}
134138

135139
#if defined(HOST_APPLE) && defined(HOST_ARM64)
@@ -306,13 +310,14 @@ EXTERN_C void* QCALLTYPE RhAllocateThunksMapping()
306310
if (!PalVirtualProtect(pThunksSection, THUNKS_MAP_SIZE, PAGE_EXECUTE_READ))
307311
{
308312
PalVirtualFree(pNewMapping, THUNKS_MAP_SIZE * 2);
309-
return NULL;
313+
return E_FAIL;
310314
}
311315
#endif
312316

313317
PalFlushInstructionCache(pThunksSection, THUNKS_MAP_SIZE);
314318

315-
return pThunksSection;
319+
*ppThunksSection = pThunksSection;
320+
return S_OK;
316321
}
317322

318323
// FEATURE_RX_THUNKS
@@ -327,7 +332,7 @@ FCDECL0(int, RhpGetThunkBlockSize);
327332
FCDECL1(void*, RhpGetThunkDataBlockAddress, void* addr);
328333
FCDECL1(void*, RhpGetThunkStubsBlockAddress, void* addr);
329334

330-
EXTERN_C void* QCALLTYPE RhAllocateThunksMapping()
335+
EXTERN_C HRESULT QCALLTYPE RhAllocateThunksMapping(void** ppThunksSection)
331336
{
332337
static int nextThunkDataMapping = 0;
333338

@@ -342,7 +347,7 @@ EXTERN_C void* QCALLTYPE RhAllocateThunksMapping()
342347

343348
if (nextThunkDataMapping == thunkDataMappingCount)
344349
{
345-
return NULL;
350+
return E_FAIL;
346351
}
347352

348353
if (g_pThunkStubData == NULL)
@@ -353,23 +358,24 @@ EXTERN_C void* QCALLTYPE RhAllocateThunksMapping()
353358

354359
if (g_pThunkStubData == NULL)
355360
{
356-
return NULL;
361+
return E_OUTOFMEMORY;
357362
}
358363
}
359364

360365
void* pThunkDataBlock = (int8_t*)g_pThunkStubData + nextThunkDataMapping * thunkDataMappingSize;
361366

362367
if (VirtualAlloc(pThunkDataBlock, thunkDataMappingSize, MEM_COMMIT, PAGE_READWRITE) == NULL)
363368
{
364-
return NULL;
369+
return E_OUTOFMEMORY;
365370
}
366371

367372
nextThunkDataMapping++;
368373

369374
void* pThunks = RhpGetThunkStubsBlockAddress(pThunkDataBlock);
370375
ASSERT(RhpGetThunkDataBlockAddress(pThunks) == pThunkDataBlock);
371376

372-
return pThunks;
377+
*ppThunksSection = pThunks;
378+
return S_OK;
373379
}
374380

375381
#else // FEATURE_FIXED_POOL_THUNKS
@@ -380,7 +386,7 @@ FCDECL0(int, RhpGetNumThunksPerBlock);
380386
FCDECL0(int, RhpGetThunkSize);
381387
FCDECL0(int, RhpGetThunkBlockSize);
382388

383-
EXTERN_C void* QCALLTYPE RhAllocateThunksMapping()
389+
EXTERN_C HRESULT QCALLTYPE RhAllocateThunksMapping(void** ppThunksSection)
384390
{
385391
static void* pThunksTemplateAddress = NULL;
386392

@@ -409,7 +415,7 @@ EXTERN_C void* QCALLTYPE RhAllocateThunksMapping()
409415
int templateRva = (int)((uint8_t*)RhpGetThunksBase() - pModuleBase);
410416

411417
if (!PalAllocateThunksFromTemplate((HANDLE)pModuleBase, templateRva, templateSize, &pThunkMap))
412-
return NULL;
418+
return E_OUTOFMEMORY;
413419
}
414420

415421
if (!PalMarkThunksAsValidCallTargets(
@@ -422,10 +428,11 @@ EXTERN_C void* QCALLTYPE RhAllocateThunksMapping()
422428
if (pThunkMap != pThunksTemplateAddress)
423429
PalFreeThunksFromTemplate(pThunkMap, templateSize);
424430

425-
return NULL;
431+
return E_FAIL;
426432
}
427433

428-
return pThunkMap;
434+
*ppThunksSection = pThunkMap;
435+
return S_OK;
429436
}
430437

431438
#endif // FEATURE_RX_THUNKS

src/coreclr/nativeaot/Runtime/portable.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,9 +365,9 @@ FCIMPL2(Object *, RhpCheckedXchg, Object ** location, Object * value)
365365
}
366366
FCIMPLEND
367367

368-
FCIMPL0(void*, RhAllocateThunksMapping)
368+
FCIMPL1(HRESULT, RhAllocateThunksMapping, void ** ppThunksSection)
369369
{
370-
return NULL;
370+
return E_FAIL;
371371
}
372372
FCIMPLEND
373373

src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -711,18 +711,12 @@ public static string TryGetMethodDisplayStringFromIp(IntPtr ip)
711711

712712
public static object CreateThunksHeap(IntPtr commonStubAddress)
713713
{
714-
object? newHeap = ThunksHeap.CreateThunksHeap(commonStubAddress);
715-
if (newHeap == null)
716-
throw new OutOfMemoryException();
717-
return newHeap;
714+
return ThunksHeap.CreateThunksHeap(commonStubAddress);
718715
}
719716

720717
public static IntPtr AllocateThunk(object thunksHeap)
721718
{
722-
IntPtr newThunk = ((ThunksHeap)thunksHeap).AllocateThunk();
723-
if (newThunk == IntPtr.Zero)
724-
throw new OutOfMemoryException();
725-
return newThunk;
719+
return ((ThunksHeap)thunksHeap).AllocateThunk();
726720
}
727721

728722
public static void FreeThunk(object thunksHeap, IntPtr thunkAddress)

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ internal static IntPtr RhHandleAllocDependent(object primary, object secondary)
358358
internal static extern int RhpGetThunkBlockSize();
359359

360360
[LibraryImport(RuntimeLibrary, EntryPoint = "RhAllocateThunksMapping")]
361-
internal static partial IntPtr RhAllocateThunksMapping();
361+
internal static unsafe partial int RhAllocateThunksMapping(IntPtr* ppMapping);
362362

363363
//
364364
// calls to runtime for type equality checks

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/ThunkPool.cs

Lines changed: 35 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -87,43 +87,26 @@ private unsafe ThunksHeap(IntPtr commonStubAddress)
8787

8888
_allocatedBlocks = new AllocatedBlock();
8989

90-
IntPtr thunkStubsBlock;
91-
lock (this)
92-
{
93-
thunkStubsBlock = ThunkBlocks.GetNewThunksBlock();
94-
}
95-
96-
if (thunkStubsBlock != IntPtr.Zero)
97-
{
98-
IntPtr thunkDataBlock = RuntimeImports.RhpGetThunkDataBlockAddress(thunkStubsBlock);
90+
IntPtr thunkStubsBlock = ThunkBlocks.GetNewThunksBlock();
91+
IntPtr thunkDataBlock = RuntimeImports.RhpGetThunkDataBlockAddress(thunkStubsBlock);
9992

100-
// Address of the first thunk data cell should be at the beginning of the thunks data block (page-aligned)
101-
Debug.Assert(((nuint)(nint)thunkDataBlock % Constants.PageSize) == 0);
93+
// Address of the first thunk data cell should be at the beginning of the thunks data block (page-aligned)
94+
Debug.Assert(((nuint)(nint)thunkDataBlock % Constants.PageSize) == 0);
10295

103-
// Update the last pointer value in the thunks data section with the value of the common stub address
104-
*(IntPtr*)(thunkDataBlock + (int)(Constants.PageSize - IntPtr.Size)) = commonStubAddress;
105-
Debug.Assert(*(IntPtr*)(thunkDataBlock + (int)(Constants.PageSize - IntPtr.Size)) == commonStubAddress);
96+
// Update the last pointer value in the thunks data section with the value of the common stub address
97+
*(IntPtr*)(thunkDataBlock + (int)(Constants.PageSize - IntPtr.Size)) = commonStubAddress;
98+
Debug.Assert(*(IntPtr*)(thunkDataBlock + (int)(Constants.PageSize - IntPtr.Size)) == commonStubAddress);
10699

107-
// Set the head and end of the linked list
108-
_nextAvailableThunkPtr = thunkDataBlock;
109-
_lastThunkPtr = _nextAvailableThunkPtr + Constants.ThunkDataSize * (Constants.NumThunksPerBlock - 1);
100+
// Set the head and end of the linked list
101+
_nextAvailableThunkPtr = thunkDataBlock;
102+
_lastThunkPtr = _nextAvailableThunkPtr + Constants.ThunkDataSize * (Constants.NumThunksPerBlock - 1);
110103

111-
_allocatedBlocks._blockBaseAddress = thunkStubsBlock;
112-
}
104+
_allocatedBlocks._blockBaseAddress = thunkStubsBlock;
113105
}
114106

115-
public static unsafe ThunksHeap? CreateThunksHeap(IntPtr commonStubAddress)
107+
public static unsafe ThunksHeap CreateThunksHeap(IntPtr commonStubAddress)
116108
{
117-
try
118-
{
119-
ThunksHeap newHeap = new ThunksHeap(commonStubAddress);
120-
121-
if (newHeap._nextAvailableThunkPtr != IntPtr.Zero)
122-
return newHeap;
123-
}
124-
catch (Exception) { }
125-
126-
return null;
109+
return new ThunksHeap(commonStubAddress);
127110
}
128111

129112
// TODO: Feature
@@ -134,47 +117,30 @@ private unsafe ThunksHeap(IntPtr commonStubAddress)
134117
//
135118
// Note: Expected to be called under lock
136119
//
137-
private unsafe bool ExpandHeap()
120+
private unsafe void ExpandHeap()
138121
{
139-
AllocatedBlock newBlockInfo;
140-
141-
try
142-
{
143-
newBlockInfo = new AllocatedBlock();
144-
}
145-
catch (Exception)
146-
{
147-
return false;
148-
}
122+
AllocatedBlock newBlockInfo = new AllocatedBlock();
149123

150124
IntPtr thunkStubsBlock = ThunkBlocks.GetNewThunksBlock();
125+
IntPtr thunkDataBlock = RuntimeImports.RhpGetThunkDataBlockAddress(thunkStubsBlock);
151126

152-
if (thunkStubsBlock != IntPtr.Zero)
153-
{
154-
IntPtr thunkDataBlock = RuntimeImports.RhpGetThunkDataBlockAddress(thunkStubsBlock);
155-
156-
// Address of the first thunk data cell should be at the beginning of the thunks data block (page-aligned)
157-
Debug.Assert(((nuint)(nint)thunkDataBlock % Constants.PageSize) == 0);
158-
159-
// Update the last pointer value in the thunks data section with the value of the common stub address
160-
*(IntPtr*)(thunkDataBlock + (int)(Constants.PageSize - IntPtr.Size)) = _commonStubAddress;
161-
Debug.Assert(*(IntPtr*)(thunkDataBlock + (int)(Constants.PageSize - IntPtr.Size)) == _commonStubAddress);
162-
163-
// Link the last entry in the old list to the first entry in the new list
164-
*((IntPtr*)_lastThunkPtr) = thunkDataBlock;
127+
// Address of the first thunk data cell should be at the beginning of the thunks data block (page-aligned)
128+
Debug.Assert(((nuint)(nint)thunkDataBlock % Constants.PageSize) == 0);
165129

166-
// Update the pointer to the last entry in the list
167-
_lastThunkPtr = *((IntPtr*)_lastThunkPtr) + Constants.ThunkDataSize * (Constants.NumThunksPerBlock - 1);
130+
// Update the last pointer value in the thunks data section with the value of the common stub address
131+
*(IntPtr*)(thunkDataBlock + (int)(Constants.PageSize - IntPtr.Size)) = _commonStubAddress;
132+
Debug.Assert(*(IntPtr*)(thunkDataBlock + (int)(Constants.PageSize - IntPtr.Size)) == _commonStubAddress);
168133

169-
newBlockInfo._blockBaseAddress = thunkStubsBlock;
170-
newBlockInfo._nextBlock = _allocatedBlocks;
134+
// Link the last entry in the old list to the first entry in the new list
135+
*((IntPtr*)_lastThunkPtr) = thunkDataBlock;
171136

172-
_allocatedBlocks = newBlockInfo;
137+
// Update the pointer to the last entry in the list
138+
_lastThunkPtr = *((IntPtr*)_lastThunkPtr) + Constants.ThunkDataSize * (Constants.NumThunksPerBlock - 1);
173139

174-
return true;
175-
}
140+
newBlockInfo._blockBaseAddress = thunkStubsBlock;
141+
newBlockInfo._nextBlock = _allocatedBlocks;
176142

177-
return false;
143+
_allocatedBlocks = newBlockInfo;
178144
}
179145

180146
public unsafe IntPtr AllocateThunk()
@@ -192,10 +158,7 @@ public unsafe IntPtr AllocateThunk()
192158

193159
if (nextNextAvailableThunkPtr == IntPtr.Zero)
194160
{
195-
if (!ExpandHeap())
196-
{
197-
return IntPtr.Zero;
198-
}
161+
ExpandHeap();
199162

200163
nextAvailableThunkPtr = _nextAvailableThunkPtr;
201164
nextNextAvailableThunkPtr = *((IntPtr*)(nextAvailableThunkPtr));
@@ -347,19 +310,12 @@ public static unsafe IntPtr GetNewThunksBlock()
347310
}
348311
else
349312
{
350-
nextThunksBlock = RuntimeImports.RhAllocateThunksMapping();
351-
352-
if (nextThunksBlock == IntPtr.Zero)
353-
{
354-
// We either ran out of memory and can't do anymore mappings of the thunks templates sections,
355-
// or we are using the managed runtime services fallback, which doesn't provide the
356-
// file mapping feature (ex: older version of mrt100.dll, or no mrt100.dll at all).
357-
358-
// The only option is for the caller to attempt and recycle unused thunks to be able to
359-
// find some free entries.
360-
361-
return IntPtr.Zero;
362-
}
313+
nextThunksBlock = IntPtr.Zero;
314+
int result = RuntimeImports.RhAllocateThunksMapping(&nextThunksBlock);
315+
if (result == HResults.E_OUTOFMEMORY)
316+
throw new OutOfMemoryException();
317+
else if (result != HResults.S_OK)
318+
throw new PlatformNotSupportedException(SR.PlatformNotSupported_DynamicEntrypoint);
363319

364320
// Each mapping consists of multiple blocks of thunk stubs/data pairs. Keep track of those
365321
// so that we do not create a new mapping until all blocks in the sections we just mapped are consumed

src/libraries/Common/src/System/HResults.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ internal static partial class HResults
121121
internal const int E_HANDLE = unchecked((int)0x80070006);
122122
internal const int E_INVALIDARG = unchecked((int)0x80070057);
123123
internal const int E_NOTIMPL = unchecked((int)0x80004001);
124+
internal const int E_OUTOFMEMORY = unchecked((int)0x8007000E);
124125
internal const int E_POINTER = unchecked((int)0x80004003);
125126
internal const int ERROR_MRM_MAP_NOT_FOUND = unchecked((int)0x80073B1F);
126127
internal const int ERROR_TIMEOUT = unchecked((int)0x800705B4);

0 commit comments

Comments
 (0)