diff options
author | Yi Zhang <yzha@microsoft.com> | 2017-04-23 08:13:26 +0300 |
---|---|---|
committer | Yi Zhang <yzha@microsoft.com> | 2017-04-23 08:13:26 +0300 |
commit | 9cc73cb1a6aceb2713baa2e04e4e3e4ca318bdaf (patch) | |
tree | 818266bc989da9e23e39dda7f49f4d2f233ceea2 /src/Native/Runtime/RuntimeInstance.cpp | |
parent | 30fb5266320b520fd7258b898ae17b914e42caca (diff) |
Enable CFG support in ProjectN
Today our ProjectN images are not CFG-enabled, but CFG will kick in for applications that have CFG enabled, in particular, WWAHost (for JavaScript apps). Today with those apps and dynamic interop enabled we would trigger a CFG check as those thunks are never registered under CFG.
There are a couple of ways to achieve this. 1) You could take advantage of the CFG table which OS loader understands and would remap in MapViewOfFile. In order to make those thunks show up in CFG, you need to give them symbolic names. According to my testing, it only seem to work if we map the full mrt100_app.dll, which isn't ideal. 2) Use SetProcessValidCallTargets and supply the thunks. I went with SetProcessValidCallTargets, which works, but has its own set of challenges (if we discover that OS matrix we need to support with CFG becomes unsustainable with this approach we can revisit):
1. Our ARM test environment is on 8.1, while SetProcessValidCallTargets is Win10+.
Normally you would fix this by either 1) LoadLibrary/GetProcAddress or 2) delay loading. Unfortunately none of these work today because LoadLibrary is not part of WACK (and has a non-trivial cost to support) and 2) SetProcessValidCallTargets is on kernel32 and not delay loadable.
Fortunately there is a API set available load api-ms-win-core-memory-l1-1-3.dll which will has this API. It is effectively forwarded to kernelbase.dll but for the purpose of delay loading it works perfectly. When the API is not found, we would redirect the API to a predefined failure stub that simply returns true.
It is worth noting that if we opt to use LoadLibrary/GetProcAddress (which you can't do under UWP), calling SetProcessValidCallTarget would trigger CFG check as it is declared __declspec(guard(nocf)) for security reasons. Fortunately delay loading works fine.
2. Calling SetProcessValidCallTargets would fail on non-CFG-enabled processes. This can be fixed by calling GetProcessMitigationPolicy to determine whether the process is CFG-enabled.
3. SetProcessValidCallTargets require our thunks to be 0x10 aligned. x64/ARM is already 0x10 aligned but x86 is only aligned on 0x4. This means we'll need to waste 0xc for each thunk on x86. This a bit unfortunate but I doubt anyone would care. If this ends up wasting too much space we can revisit and optimize as needed.
4. SetProcessValidCallTargets is not allowed in WACK today. Fortunately it is already on pre-approved WACK list for RS2. It does mean that you won't be able to submit apps with mrt100_app.dll today but that's probably fine - our plan for the new toolchain is RS3+.
5. Our toolsets in razzle enlistment only has 8.1 the latest. For now I've work arounded by manually declaring the imports and copy the latest mincore.lib to rh/public/minwin folder. This has a unfortunate side effect that it also 'upgrades' RaiseFailFastException to a new API set that is not available down-level 8.1. I've made RaiseFailFastException also delay loading so that we wouldn't immediately fail to load under ARM + Win 8.1. I doubt any one cares about how N failfast under 8.1.
6. Similarly, ToF also has a copy of Win8 toolset in its own ExternalAPI (not the razzle one) and it doesn't support creating a CFG-aware binary. One possiblity is to use JavaScript app like in the bug, but you also need a reliable way to construct thunks and send it over to javaScript as v-table so that JavaScript can call it. This is basically dyanmic interop feature so the best way to cover this today without CFG-aware toolset is in dyanmic interop testing with full dyanmic interop mode on (everything dynamic). Once we have a CFG-aware toolset (I suspect this would only happen when we move out of ToF), we can have the main C++ app call reverse p/invoke thunks.
I've introduced a new API PalMarkThunksAsValidCallTargets to do the heavylifting - to allocate the correct data structure ro register the thunks and call SetProcessValidCallTargets. Rest of the stuff are less interesting.
After this change, I've verified the Yahoo app with dynamic interop turned on can proceed further (but still eventually timed out). I've also done a few more testing:
* ARM 8.1 private interop run,
* x86 private interop run,
* thunkpool tests
* x86chk precheckin
* verified ARM phone does have SetProcessValidCallTargets API.
* Build and ran all CoreRT tests
[tfs-changeset: 1655595]
Diffstat (limited to 'src/Native/Runtime/RuntimeInstance.cpp')
-rw-r--r-- | src/Native/Runtime/RuntimeInstance.cpp | 41 |
1 files changed, 32 insertions, 9 deletions
diff --git a/src/Native/Runtime/RuntimeInstance.cpp b/src/Native/Runtime/RuntimeInstance.cpp index 665b77636..608aba01d 100644 --- a/src/Native/Runtime/RuntimeInstance.cpp +++ b/src/Native/Runtime/RuntimeInstance.cpp @@ -879,29 +879,52 @@ COOP_PINVOKE_HELPER(void *, RhGetGcStaticFieldData, (EEType * pEEType)) COOP_PINVOKE_HELPER(void*, RhpGetThunksBase, ()); COOP_PINVOKE_HELPER(int, RhpGetNumThunkBlocksPerMapping, ()); +COOP_PINVOKE_HELPER(int, RhpGetNumThunksPerBlock, ()); +COOP_PINVOKE_HELPER(int, RhpGetThunkSize, ()); +COOP_PINVOKE_HELPER(int, RhpGetThunkBlockSize, ()); + EXTERN_C REDHAWK_API void* __cdecl RhAllocateThunksMapping() { static void* pThunksTemplateAddress = NULL; + void *pThunkMap = NULL; + + int thunkBlocksPerMapping = RhpGetNumThunkBlocksPerMapping(); + int thunkBlockSize = RhpGetThunkBlockSize(); + int templateSize = thunkBlocksPerMapping * thunkBlockSize; + if (pThunksTemplateAddress == NULL) { // First, we use the thunks directly from the thunks template sections in the module until all // thunks in that template are used up. pThunksTemplateAddress = RhpGetThunksBase(); - return pThunksTemplateAddress; + pThunkMap = pThunksTemplateAddress; } + else + { + // We've already used the thunks template in the module for some previous thunks, and we + // cannot reuse it here. Now we need to create a new mapping of the thunks section in order to have + // more thunks - // We've already used the thunks template in the module for some previous thunks, and we - // cannot reuse it here. Now we need to create a new mapping of the thunks section in order to have - // more thunks + UInt8* pModuleBase = (UInt8*)PalGetModuleHandleFromPointer(pThunksTemplateAddress); + int templateRva = (int)((UInt8*)RhpGetThunksBase() - pModuleBase); - UInt8* pModuleBase = (UInt8*)PalGetModuleHandleFromPointer(pThunksTemplateAddress); - int templateRva = (int)((UInt8*)RhpGetThunksBase() - pModuleBase); - int templateSize = RhpGetNumThunkBlocksPerMapping() * OS_PAGE_SIZE * 2; + if (!PalAllocateThunksFromTemplate((HANDLE)pModuleBase, templateRva, templateSize, &pThunkMap)) + return NULL; + } + + if (!PalMarkThunksAsValidCallTargets( + pThunkMap, + RhpGetThunkSize(), + RhpGetNumThunksPerBlock(), + thunkBlockSize, + thunkBlocksPerMapping)) + { + if (pThunkMap != pThunksTemplateAddress) + PalFreeThunksFromTemplate(pThunkMap); - void* pThunkMap = NULL; - if (PalAllocateThunksFromTemplate((HANDLE)pModuleBase, templateRva, templateSize, &pThunkMap) == FALSE) return NULL; + } return pThunkMap; } |