Skip to content

Commit 1096864

Browse files
committed
[MERGE #5859 @LouisLaf] CFG support for ChakraCore
Merge pull request #5859 from LouisLaf:cfg 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).
2 parents 7ff08c9 + d07f58b commit 1096864

19 files changed

+222
-252
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/Backend/GlobOpt.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5211,7 +5211,7 @@ GlobOpt::ValueNumberLdElemDst(IR::Instr **pInstr, Value *srcVal)
52115211
}
52125212
}
52135213
}
5214-
5214+
52155215
IR::IndirOpnd *src = instr->GetSrc1()->AsIndirOpnd();
52165216
const ValueType baseValueType(src->GetBaseOpnd()->GetValueType());
52175217
if (instr->DoStackArgsOpt() ||

lib/Common/CommonDefines.h

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

15+
// CFG was never enabled for ARM32 and requires WIN10 SDK
16+
#if !defined(_M_ARM) && defined(_WIN32) && defined(NTDDI_WIN10)
17+
#define _CONTROL_FLOW_GUARD 1
18+
#endif
1519

1620
//----------------------------------------------------------------------------------------------------
1721
// Default debug/fretest/release flags values
@@ -319,7 +323,9 @@
319323
#endif
320324

321325
// Other features
322-
// #define CHAKRA_CORE_DOWN_COMPAT 1
326+
#if defined(_CHAKRACOREBUILD)
327+
# define CHAKRA_CORE_DOWN_COMPAT 1
328+
#endif
323329

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

lib/Common/Core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ add_library (Chakra.Common.Core OBJECT
1111
DelayLoadLibrary.cpp
1212
EtwTraceCore.cpp
1313
FaultInjection.cpp
14+
GlobalSecurityPolicy.cpp
1415
Output.cpp
1516
PerfCounter.cpp
1617
PerfCounterImpl.cpp

lib/Common/Core/GlobalSecurityPolicy.cpp

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

66
#include "CommonCorePch.h"
77

8-
#pragma section(".mrdata", read)
8+
#ifdef _WIN32
9+
10+
#include <VersionHelpers.h>
11+
912

1013
CriticalSection GlobalSecurityPolicy::s_policyCS;
14+
GlobalSecurityPolicy GlobalSecurityObject;
15+
16+
#pragma section(".mrdata", read)
17+
18+
// Note: 'volatile' is necessary here otherwise the compiler assumes these are constants initialized to '0' and will constant propagate them...
19+
__declspec(allocate(".mrdata")) volatile GlobalSecurityPolicy::ReadOnlyData GlobalSecurityPolicy::readOnlyData =
20+
{
21+
#if defined(_CONTROL_FLOW_GUARD)
22+
nullptr,
23+
nullptr,
24+
#endif
25+
false,
26+
false,
27+
false
28+
};
29+
30+
bool
31+
GlobalSecurityPolicy::IsCFGEnabled()
32+
{
33+
return readOnlyData.isCFGEnabled && !PHASE_OFF1(Js::CFGPhase);
34+
}
1135

12-
__declspec(allocate(".mrdata"))
13-
volatile bool GlobalSecurityPolicy::s_ro_disableSetProcessValidCallTargets = false;
36+
bool
37+
GlobalSecurityPolicy::InitIsCFGEnabled()
38+
{
39+
#if defined(_CONTROL_FLOW_GUARD)
40+
PROCESS_MITIGATION_CONTROL_FLOW_GUARD_POLICY CfgPolicy;
41+
BOOL isGetMitigationPolicySucceeded = GlobalSecurityPolicy::GetMitigationPolicyForProcess(
42+
GetCurrentProcess(),
43+
ProcessControlFlowGuardPolicy,
44+
&CfgPolicy,
45+
sizeof(CfgPolicy));
46+
AssertOrFailFast(isGetMitigationPolicySucceeded);
47+
return CfgPolicy.EnableControlFlowGuard;
48+
49+
#else
50+
return false;
51+
#endif // _CONTROL_FLOW_GUARD
52+
}
53+
54+
GlobalSecurityPolicy::GlobalSecurityPolicy()
55+
{
56+
#if defined(_CONTROL_FLOW_GUARD)
57+
AutoCriticalSection autocs(&s_policyCS);
58+
DWORD oldProtect;
59+
60+
// Make sure this is called only once
61+
AssertOrFailFast(!readOnlyData.isInitialized);
62+
63+
#if defined(CHAKRA_CORE_DOWN_COMPAT)
64+
if (AutoSystemInfo::Data.IsWinThresholdOrLater())
65+
#endif
66+
{
67+
// Make readOnlyData read-write
68+
BOOL res = VirtualProtect((LPVOID)&readOnlyData, sizeof(ReadOnlyData), PAGE_READWRITE, &oldProtect);
69+
if ((res == FALSE) || (oldProtect != PAGE_READONLY))
70+
{
71+
RaiseFailFastException(nullptr, nullptr, FAIL_FAST_GENERATE_EXCEPTION_ADDRESS);
72+
}
73+
74+
readOnlyData.isInitialized = true;
75+
76+
EnsureFromSystemDirOnly();
77+
78+
if (m_hModule)
79+
{
80+
readOnlyData.pfnGetProcessMitigationPolicy = (PFNCGetMitigationPolicyForProcess)GetFunction("GetProcessMitigationPolicy");
81+
if (readOnlyData.pfnGetProcessMitigationPolicy == nullptr)
82+
{
83+
RaiseFailFastException(nullptr, nullptr, FAIL_FAST_GENERATE_EXCEPTION_ADDRESS);
84+
}
85+
86+
readOnlyData.isCFGEnabled = InitIsCFGEnabled();
87+
88+
if (readOnlyData.isCFGEnabled)
89+
{
90+
readOnlyData.pfnSetProcessValidCallTargets = (PFNCSetProcessValidCallTargets)GetFunction("SetProcessValidCallTargets");
91+
if (readOnlyData.pfnSetProcessValidCallTargets == nullptr)
92+
{
93+
RaiseFailFastException(nullptr, nullptr, FAIL_FAST_GENERATE_EXCEPTION_ADDRESS);
94+
}
95+
}
96+
}
97+
98+
// Make readOnlyData read-only again.
99+
res = VirtualProtect((LPVOID)&readOnlyData, sizeof(ReadOnlyData), PAGE_READONLY, &oldProtect);
100+
if ((res == FALSE) || (oldProtect != PAGE_READWRITE))
101+
{
102+
RaiseFailFastException(nullptr, nullptr, FAIL_FAST_GENERATE_EXCEPTION_ADDRESS);
103+
}
104+
105+
#if defined (NTBUILD)
106+
AssertOrFailFast(readOnlyData.isCFGEnabled);
107+
#endif
108+
}
109+
110+
#endif //_CONTROL_FLOW_GUARD
111+
}
14112

15113
void
16114
GlobalSecurityPolicy::DisableSetProcessValidCallTargets()
17115
{
18116
// One-way transition from allowing SetProcessValidCallTargets to disabling
19117
// the API.
20-
if (!s_ro_disableSetProcessValidCallTargets)
118+
if (!readOnlyData.disableSetProcessValidCallTargets)
21119
{
22120
AutoCriticalSection autocs(&s_policyCS);
23121
DWORD oldProtect;
24122

25-
BOOL res = VirtualProtect((LPVOID)&s_ro_disableSetProcessValidCallTargets, sizeof(s_ro_disableSetProcessValidCallTargets), PAGE_READWRITE, &oldProtect);
123+
BOOL res = VirtualProtect((LPVOID)&readOnlyData, sizeof(ReadOnlyData), PAGE_READWRITE, &oldProtect);
26124
if ((res == FALSE) || (oldProtect != PAGE_READONLY))
27125
{
28126
RaiseFailFastException(nullptr, nullptr, FAIL_FAST_GENERATE_EXCEPTION_ADDRESS);
29127
}
30128

31-
s_ro_disableSetProcessValidCallTargets = true;
129+
readOnlyData.disableSetProcessValidCallTargets = true;
32130

33-
res = VirtualProtect((LPVOID)&s_ro_disableSetProcessValidCallTargets, sizeof(s_ro_disableSetProcessValidCallTargets), PAGE_READONLY, &oldProtect);
131+
res = VirtualProtect((LPVOID)&readOnlyData, sizeof(ReadOnlyData), PAGE_READONLY, &oldProtect);
34132
if ((res == FALSE) || (oldProtect != PAGE_READWRITE))
35133
{
36134
RaiseFailFastException(nullptr, nullptr, FAIL_FAST_GENERATE_EXCEPTION_ADDRESS);
@@ -41,5 +139,20 @@ GlobalSecurityPolicy::DisableSetProcessValidCallTargets()
41139
bool
42140
GlobalSecurityPolicy::IsSetProcessValidCallTargetsAllowed()
43141
{
44-
return !s_ro_disableSetProcessValidCallTargets;
142+
return !readOnlyData.disableSetProcessValidCallTargets;
143+
}
144+
145+
#if defined(_CONTROL_FLOW_GUARD)
146+
BOOL
147+
DECLSPEC_GUARDNOCF GlobalSecurityPolicy::GetMitigationPolicyForProcess(HANDLE hProcess, PROCESS_MITIGATION_POLICY mitigationPolicy, PVOID lpBuffer, SIZE_T dwLength)
148+
{
149+
return GlobalSecurityPolicy::readOnlyData.pfnGetProcessMitigationPolicy(hProcess, mitigationPolicy, lpBuffer, dwLength);
150+
}
151+
152+
BOOL
153+
DECLSPEC_GUARDNOCF GlobalSecurityPolicy::SetProcessValidCallTargets(HANDLE hProcess, PVOID virtualAddress, SIZE_T regionSize, ULONG numberOfOffsets, PCFG_CALL_TARGET_INFO offsetInformation)
154+
{
155+
return GlobalSecurityPolicy::readOnlyData.pfnSetProcessValidCallTargets(hProcess, virtualAddress, regionSize, numberOfOffsets, offsetInformation);
45156
}
157+
#endif //_CONTROL_FLOW_GUARD
158+
#endif // _WIN32

lib/Common/Core/GlobalSecurityPolicy.h

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

7-
class GlobalSecurityPolicy
7+
#include "DelayLoadLibrary.h"
8+
9+
class GlobalSecurityPolicy : private DelayLoadLibrary
810
{
911
public:
12+
#ifdef _WIN32
13+
#if defined(_CONTROL_FLOW_GUARD)
14+
typedef BOOL FNCGetMitigationPolicyForProcess(HANDLE, PROCESS_MITIGATION_POLICY, PVOID, SIZE_T);
15+
typedef FNCGetMitigationPolicyForProcess* PFNCGetMitigationPolicyForProcess;
16+
17+
typedef BOOL FNCSetProcessValidCallTargets(HANDLE, PVOID, SIZE_T, ULONG, PCFG_CALL_TARGET_INFO);
18+
typedef FNCSetProcessValidCallTargets* PFNCSetProcessValidCallTargets;
19+
#endif
20+
GlobalSecurityPolicy();
21+
1022
static void DisableSetProcessValidCallTargets();
1123
static bool IsSetProcessValidCallTargetsAllowed();
24+
static bool IsCFGEnabled();
25+
26+
#if defined(_CONTROL_FLOW_GUARD)
27+
static FNCGetMitigationPolicyForProcess GetMitigationPolicyForProcess;
28+
static FNCSetProcessValidCallTargets SetProcessValidCallTargets;
29+
#endif
30+
LPCTSTR GetLibraryName() const { return _u("api-ms-win-core-memory-l1-1-3.dll"); }
1231

1332
private:
1433
static CriticalSection s_policyCS;
1534

16-
static volatile bool s_ro_disableSetProcessValidCallTargets;
35+
volatile static struct ReadOnlyData {
36+
#if defined(_CONTROL_FLOW_GUARD)
37+
PFNCGetMitigationPolicyForProcess pfnGetProcessMitigationPolicy;
38+
PFNCSetProcessValidCallTargets pfnSetProcessValidCallTargets;
39+
#endif
40+
bool disableSetProcessValidCallTargets;
41+
bool isCFGEnabled;
42+
bool isInitialized;
43+
} readOnlyData;
44+
45+
static bool InitIsCFGEnabled();
46+
#else
47+
static bool IsCFGEnabled() { return false; }
48+
#endif
1749
};

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();

0 commit comments

Comments
 (0)