Skip to content

Commit c0c3de7

Browse files
committed
CFG support for ChakraCore
Enabled CFG in ChakraCore in a way which keeps the binary running on pre-Win10 (pre-CFG) OSs. To make this safe, the necessary APIs are delayloaded at initialization time, and the function pointers and then put in a read-only data section. Note: For CFG to be enabled, the host needs to be marked as supporting Win10 by linking with /subsystem, os as supporting multiple OS version through a manifest (see https://docs.microsoft.com/en-us/windows/desktop/SysInfo/targeting-your-application-at-windows-8-1).
1 parent 4a18b8a commit c0c3de7

17 files changed

+201
-250
lines changed

Build/Common.Build.props

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565
<!-- /Gy -->
6666
<FunctionLevelLinking>true</FunctionLevelLinking>
6767
<!-- /GF -->
68+
<ControlFlowGuard Condition="'$(Platform)'!='ARM'">Guard</ControlFlowGuard>
69+
<!-- /guard:cf -->
6870
<StringPooling>true</StringPooling>
6971
<!-- /MD -->
7072
<RuntimeLibrary Condition="'$(RuntimeLib)'!='static_library'">MultiThreadedDLL</RuntimeLibrary>

bin/ch/ch.manifest

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
3+
<assemblyIdentity
4+
name="Microsoft.Chakra.ch.exe"
5+
version="1.0.0.0"
6+
type="win32" />
7+
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
8+
<application>
9+
<!-- Windows 10 -->
10+
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
11+
<!-- Windows 8.1 -->
12+
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
13+
<!-- Windows 8 -->
14+
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
15+
<!-- Windows 7 -->
16+
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
17+
<!-- Windows Vista -->
18+
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
19+
</application>
20+
</compatibility>
21+
</assembly>

bin/ch/ch.vcxproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@
123123
<Authenticode>Microsoft</Authenticode>
124124
</FilesToSign>
125125
</ItemGroup>
126+
<ItemGroup>
127+
<Manifest Include="ch.manifest" />
128+
</ItemGroup>
126129
<Import Project="$(BuildConfigPropsPath)Chakra.Build.targets" Condition="exists('$(BuildConfigPropsPath)Chakra.Build.targets')" />
127130
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
128-
</Project>
131+
</Project>

lib/Common/CommonDefines.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
#include "Warnings.h"
1313
#include "ChakraCoreVersion.h"
1414

15+
#if !defined(_M_ARM)
16+
#define _CONTROL_FLOW_GUARD 1
17+
#endif
1518

1619
//----------------------------------------------------------------------------------------------------
1720
// Default debug/fretest/release flags values
@@ -319,7 +322,9 @@
319322
#endif
320323

321324
// Other features
322-
// #define CHAKRA_CORE_DOWN_COMPAT 1
325+
#if defined(_CHAKRACOREBUILD)
326+
# define CHAKRA_CORE_DOWN_COMPAT 1
327+
#endif
323328

324329
// todo:: Enable vectorcall on NTBUILD. OS#13609380
325330
#if defined(_WIN32) && !defined(NTBUILD) && defined(_M_IX86)

lib/Common/Core/GlobalSecurityPolicy.cpp

Lines changed: 117 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,129 @@
55

66
#include "CommonCorePch.h"
77

8-
#pragma section(".mrdata", read)
8+
#include <VersionHelpers.h>
9+
910

1011
CriticalSection GlobalSecurityPolicy::s_policyCS;
12+
GlobalSecurityPolicy GlobalSecurityObject;
13+
14+
#pragma section(".mrdata", read)
15+
16+
// Note: 'volatile' is necessary here otherwise the compiler assumes these are constants initialized to '0' and will constant propagate them...
17+
__declspec(allocate(".mrdata")) volatile GlobalSecurityPolicy::ReadOnlyData GlobalSecurityPolicy::readOnlyData =
18+
{
19+
nullptr,
20+
nullptr,
21+
false,
22+
false,
23+
false
24+
};
25+
26+
bool
27+
GlobalSecurityPolicy::IsCFGEnabled()
28+
{
29+
return readOnlyData.isCFGEnabled && !PHASE_OFF1(Js::CFGPhase);
30+
}
31+
32+
bool
33+
GlobalSecurityPolicy::InitIsCFGEnabled()
34+
{
35+
#if defined(_CONTROL_FLOW_GUARD)
36+
PROCESS_MITIGATION_CONTROL_FLOW_GUARD_POLICY CfgPolicy;
37+
BOOL isGetMitigationPolicySucceeded = GlobalSecurityPolicy::GetMitigationPolicyForProcess(
38+
GetCurrentProcess(),
39+
ProcessControlFlowGuardPolicy,
40+
&CfgPolicy,
41+
sizeof(CfgPolicy));
42+
AssertOrFailFast(isGetMitigationPolicySucceeded);
43+
return CfgPolicy.EnableControlFlowGuard;
44+
45+
#else
46+
return false;
47+
#endif // _CONTROL_FLOW_GUARD
48+
}
49+
50+
GlobalSecurityPolicy::GlobalSecurityPolicy()
51+
{
52+
AutoCriticalSection autocs(&s_policyCS);
53+
DWORD oldProtect;
54+
55+
// Make sure this is called only once
56+
AssertOrFailFast(!readOnlyData.isInitialized);
57+
58+
#if defined(_CONTROL_FLOW_GUARD)
59+
#if defined(CHAKRA_CORE_DOWN_COMPAT)
60+
if (AutoSystemInfo::Data.IsWinThresholdOrLater())
61+
#endif
62+
{
63+
// Make readOnlyData read-write
64+
BOOL res = VirtualProtect((LPVOID)&readOnlyData, sizeof(ReadOnlyData), PAGE_READWRITE, &oldProtect);
65+
if ((res == FALSE) || (oldProtect != PAGE_READONLY))
66+
{
67+
RaiseFailFastException(nullptr, nullptr, FAIL_FAST_GENERATE_EXCEPTION_ADDRESS);
68+
}
69+
70+
readOnlyData.isInitialized = true;
71+
72+
EnsureFromSystemDirOnly();
73+
74+
if (m_hModule)
75+
{
76+
readOnlyData.pfnGetProcessMitigationPolicy = (PFNCGetMitigationPolicyForProcess)GetFunction("GetProcessMitigationPolicy");
77+
if (readOnlyData.pfnGetProcessMitigationPolicy == nullptr)
78+
{
79+
RaiseFailFastException(nullptr, nullptr, FAIL_FAST_GENERATE_EXCEPTION_ADDRESS);
80+
}
81+
82+
readOnlyData.isCFGEnabled = InitIsCFGEnabled();
83+
84+
if (readOnlyData.isCFGEnabled)
85+
{
86+
readOnlyData.pfnSetProcessValidCallTargets = (PFNCSetProcessValidCallTargets)GetFunction("SetProcessValidCallTargets");
87+
if (readOnlyData.pfnSetProcessValidCallTargets == nullptr)
88+
{
89+
RaiseFailFastException(nullptr, nullptr, FAIL_FAST_GENERATE_EXCEPTION_ADDRESS);
90+
}
91+
}
92+
}
93+
94+
// Make readOnlyData read-only again.
95+
res = VirtualProtect((LPVOID)&readOnlyData, sizeof(ReadOnlyData), PAGE_READONLY, &oldProtect);
96+
if ((res == FALSE) || (oldProtect != PAGE_READWRITE))
97+
{
98+
RaiseFailFastException(nullptr, nullptr, FAIL_FAST_GENERATE_EXCEPTION_ADDRESS);
99+
}
100+
101+
#if defined (NTBUILD)
102+
AssertOrFailFast(readOnlyData.isCFGEnabled);
103+
#endif
104+
}
105+
11106

12-
__declspec(allocate(".mrdata"))
13-
volatile bool GlobalSecurityPolicy::s_ro_disableSetProcessValidCallTargets = false;
107+
#else
108+
return false;
109+
#endif //_CONTROL_FLOW_GUARD
110+
}
14111

15112
void
16113
GlobalSecurityPolicy::DisableSetProcessValidCallTargets()
17114
{
18115
// One-way transition from allowing SetProcessValidCallTargets to disabling
19116
// the API.
20-
if (!s_ro_disableSetProcessValidCallTargets)
117+
if (!readOnlyData.disableSetProcessValidCallTargets)
21118
{
22119
AutoCriticalSection autocs(&s_policyCS);
23120
DWORD oldProtect;
24121

25-
BOOL res = VirtualProtect((LPVOID)&s_ro_disableSetProcessValidCallTargets, sizeof(s_ro_disableSetProcessValidCallTargets), PAGE_READWRITE, &oldProtect);
122+
BOOL res = VirtualProtect((LPVOID)&readOnlyData, sizeof(ReadOnlyData), PAGE_READWRITE, &oldProtect);
26123
if ((res == FALSE) || (oldProtect != PAGE_READONLY))
27124
{
28125
RaiseFailFastException(nullptr, nullptr, FAIL_FAST_GENERATE_EXCEPTION_ADDRESS);
29126
}
30127

31-
s_ro_disableSetProcessValidCallTargets = true;
128+
readOnlyData.disableSetProcessValidCallTargets = true;
32129

33-
res = VirtualProtect((LPVOID)&s_ro_disableSetProcessValidCallTargets, sizeof(s_ro_disableSetProcessValidCallTargets), PAGE_READONLY, &oldProtect);
130+
res = VirtualProtect((LPVOID)&readOnlyData, sizeof(ReadOnlyData), PAGE_READONLY, &oldProtect);
34131
if ((res == FALSE) || (oldProtect != PAGE_READWRITE))
35132
{
36133
RaiseFailFastException(nullptr, nullptr, FAIL_FAST_GENERATE_EXCEPTION_ADDRESS);
@@ -41,5 +138,17 @@ GlobalSecurityPolicy::DisableSetProcessValidCallTargets()
41138
bool
42139
GlobalSecurityPolicy::IsSetProcessValidCallTargetsAllowed()
43140
{
44-
return !s_ro_disableSetProcessValidCallTargets;
141+
return !readOnlyData.disableSetProcessValidCallTargets;
45142
}
143+
144+
BOOL
145+
DECLSPEC_GUARDNOCF GlobalSecurityPolicy::GetMitigationPolicyForProcess(HANDLE hProcess, PROCESS_MITIGATION_POLICY mitigationPolicy, PVOID lpBuffer, SIZE_T dwLength)
146+
{
147+
return GlobalSecurityPolicy::readOnlyData.pfnGetProcessMitigationPolicy(hProcess, mitigationPolicy, lpBuffer, dwLength);
148+
}
149+
150+
BOOL
151+
DECLSPEC_GUARDNOCF GlobalSecurityPolicy::SetProcessValidCallTargets(HANDLE hProcess, PVOID virtualAddress, SIZE_T regionSize, ULONG numberOfOffsets, PCFG_CALL_TARGET_INFO offsetInformation)
152+
{
153+
return GlobalSecurityPolicy::readOnlyData.pfnSetProcessValidCallTargets(hProcess, virtualAddress, regionSize, numberOfOffsets, offsetInformation);
154+
}

lib/Common/Core/GlobalSecurityPolicy.h

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,37 @@
44
//-------------------------------------------------------------------------------------------------------
55
#pragma once
66

7-
class GlobalSecurityPolicy
7+
#include "DelayLoadLibrary.h"
8+
9+
class GlobalSecurityPolicy : private DelayLoadLibrary
810
{
911
public:
12+
typedef BOOL FNCGetMitigationPolicyForProcess(HANDLE, PROCESS_MITIGATION_POLICY, PVOID, SIZE_T);
13+
typedef FNCGetMitigationPolicyForProcess* PFNCGetMitigationPolicyForProcess;
14+
15+
typedef BOOL FNCSetProcessValidCallTargets(HANDLE, PVOID, SIZE_T, ULONG, PCFG_CALL_TARGET_INFO);
16+
typedef FNCSetProcessValidCallTargets* PFNCSetProcessValidCallTargets;
17+
18+
GlobalSecurityPolicy();
19+
1020
static void DisableSetProcessValidCallTargets();
1121
static bool IsSetProcessValidCallTargetsAllowed();
22+
static bool IsCFGEnabled();
23+
static FNCGetMitigationPolicyForProcess GetMitigationPolicyForProcess;
24+
static FNCSetProcessValidCallTargets SetProcessValidCallTargets;
25+
26+
LPCTSTR GetLibraryName() const { return _u("api-ms-win-core-memory-l1-1-3.dll"); }
1227

1328
private:
1429
static CriticalSection s_policyCS;
1530

16-
static volatile bool s_ro_disableSetProcessValidCallTargets;
31+
volatile static struct ReadOnlyData {
32+
PFNCGetMitigationPolicyForProcess pfnGetProcessMitigationPolicy;
33+
PFNCSetProcessValidCallTargets pfnSetProcessValidCallTargets;
34+
bool disableSetProcessValidCallTargets;
35+
bool isCFGEnabled;
36+
bool isInitialized;
37+
} readOnlyData;
38+
39+
static bool InitIsCFGEnabled();
1740
};

lib/Common/Core/SysInfo.cpp

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -360,20 +360,6 @@ AutoSystemInfo::CheckForAtom() const
360360
}
361361
#endif
362362

363-
bool
364-
AutoSystemInfo::IsCFGEnabled()
365-
{
366-
#if defined(_CONTROL_FLOW_GUARD)
367-
return true
368-
#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
369-
&& IsWinThresholdOrLater() && !PHASE_OFF1(Js::CFGPhase)
370-
#endif //ENABLE_DEBUG_CONFIG_OPTIONS
371-
;
372-
#else
373-
return false;
374-
#endif //_CONTROL_FLOW_GUARD
375-
}
376-
377363
bool
378364
AutoSystemInfo::IsWin8OrLater()
379365
{

lib/Common/Core/SysInfo.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ class AutoSystemInfo : public SYSTEM_INFO
1515
uint GetAllocationGranularityPageSize() const;
1616

1717
bool DisableDebugScopeCapture() const { return this->disableDebugScopeCapture; }
18-
bool IsCFGEnabled();
1918
bool IsWin8OrLater();
2019
#if defined(_CONTROL_FLOW_GUARD)
2120
bool IsWinThresholdOrLater();

lib/Common/Memory/CustomHeap.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ BOOL Heap<TAlloc, TPreReservedAlloc>::ProtectAllocationWithExecuteReadWrite(Allo
288288
{
289289
DWORD protectFlags = 0;
290290

291-
if (AutoSystemInfo::Data.IsCFGEnabled())
291+
if (GlobalSecurityPolicy::IsCFGEnabled())
292292
{
293293
protectFlags = PAGE_EXECUTE_RW_TARGETS_NO_UPDATE;
294294
}
@@ -303,7 +303,7 @@ template<typename TAlloc, typename TPreReservedAlloc>
303303
BOOL Heap<TAlloc, TPreReservedAlloc>::ProtectAllocationWithExecuteReadOnly(__in Allocation *allocation, __in_opt char* addressInPage)
304304
{
305305
DWORD protectFlags = 0;
306-
if (AutoSystemInfo::Data.IsCFGEnabled())
306+
if (GlobalSecurityPolicy::IsCFGEnabled())
307307
{
308308
protectFlags = PAGE_EXECUTE_RO_TARGETS_NO_UPDATE;
309309
}
@@ -417,7 +417,7 @@ Allocation* Heap<TAlloc, TPreReservedAlloc>::AllocLargeObject(size_t bytes, usho
417417
if (this->processHandle == GetCurrentProcess())
418418
{
419419
DWORD protectFlags = 0;
420-
if (AutoSystemInfo::Data.IsCFGEnabled())
420+
if (GlobalSecurityPolicy::IsCFGEnabled())
421421
{
422422
protectFlags = PAGE_EXECUTE_RO_TARGETS_NO_UPDATE;
423423
}
@@ -513,7 +513,7 @@ DWORD Heap<TAlloc, TPreReservedAlloc>::EnsureAllocationWriteable(Allocation* all
513513
template<typename TAlloc, typename TPreReservedAlloc>
514514
DWORD Heap<TAlloc, TPreReservedAlloc>::EnsureAllocationExecuteWriteable(Allocation* allocation)
515515
{
516-
if (AutoSystemInfo::Data.IsCFGEnabled())
516+
if (GlobalSecurityPolicy::IsCFGEnabled())
517517
{
518518
return EnsureAllocationReadWrite<PAGE_EXECUTE_RW_TARGETS_NO_UPDATE>(allocation);
519519
}
@@ -685,7 +685,7 @@ Page* Heap<TAlloc, TPreReservedAlloc>::AllocNewPage(BucketId bucket, bool canAll
685685

686686
DWORD protectFlags = 0;
687687

688-
if (AutoSystemInfo::Data.IsCFGEnabled())
688+
if (GlobalSecurityPolicy::IsCFGEnabled())
689689
{
690690
protectFlags = PAGE_EXECUTE_RO_TARGETS_NO_UPDATE;
691691
}
@@ -885,7 +885,7 @@ bool Heap<TAlloc, TPreReservedAlloc>::FreeAllocation(Allocation* object)
885885

886886
DWORD protectFlags = 0;
887887

888-
if (AutoSystemInfo::Data.IsCFGEnabled())
888+
if (GlobalSecurityPolicy::IsCFGEnabled())
889889
{
890890
protectFlags = PAGE_EXECUTE_RO_TARGETS_NO_UPDATE;
891891
}

lib/Common/Memory/PageAllocator.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2665,7 +2665,7 @@ HeapPageAllocator<T>::ProtectPages(__in char* address, size_t pageCount, __in vo
26652665

26662666
/*Verify if we always pass the PAGE_TARGETS_NO_UPDATE flag, if the protect flag is EXECUTE*/
26672667
#if defined(_CONTROL_FLOW_GUARD)
2668-
if (AutoSystemInfo::Data.IsCFGEnabled() &&
2668+
if (GlobalSecurityPolicy::IsCFGEnabled() &&
26692669
(dwVirtualProtectFlags & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE)) &&
26702670
((dwVirtualProtectFlags & PAGE_TARGETS_NO_UPDATE) == 0))
26712671
{

0 commit comments

Comments
 (0)