process_mitigations_dyncode_unittest.cc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. // Copyright 2011 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "base/memory/raw_ptr.h"
  5. #include "sandbox/win/src/process_mitigations.h"
  6. #include <windows.h>
  7. #include <string>
  8. #include "base/files/file_util.h"
  9. #include "base/files/scoped_temp_dir.h"
  10. #include "base/path_service.h"
  11. #include "base/strings/stringprintf.h"
  12. #include "base/win/windows_version.h"
  13. #include "sandbox/win/src/process_mitigations.h"
  14. #include "sandbox/win/src/sandbox.h"
  15. #include "sandbox/win/src/target_services.h"
  16. #include "sandbox/win/tests/common/controller.h"
  17. #include "sandbox/win/tests/integration_tests/hooking_dll.h"
  18. #include "sandbox/win/tests/integration_tests/integration_tests_common.h"
  19. #include "testing/gtest/include/gtest/gtest.h"
  20. namespace {
  21. //------------------------------------------------------------------------------
  22. // Internal Defines & Functions
  23. //------------------------------------------------------------------------------
  24. // Enum the dynamic code APIs being tested, to prevent hard coded int values.
  25. enum DynCodeAPI {
  26. VIRTUALALLOC = 1,
  27. VIRTUALPROTECT,
  28. MAPVIEWCUSTOM,
  29. MAPVIEWFILE,
  30. NOTSUPPORTED // Always leave this as the last enum.
  31. };
  32. // Advanced private function declaration.
  33. void DynamicCodeTestHarness(sandbox::MitigationFlags which_mitigation,
  34. bool expect_success,
  35. bool enable_mitigation,
  36. bool with_thread_opt_out = false);
  37. // Common helper function for the different child process dynamic code tests.
  38. //
  39. // - VirtualAlloc with PAGE_EXECUTE_*
  40. // - VirtualProtect with PAGE_EXECUTE_*
  41. // - MapViewOfFile with FILE_MAP_EXECUTE | FILE_MAP_WRITE
  42. int DynamicCodeTest(DynCodeAPI which_test, wchar_t* path) {
  43. switch (which_test) {
  44. case VIRTUALALLOC: {
  45. // Test VirtualAlloc with PAGE_EXECUTE_READWRITE.
  46. //-----------------------------------------------
  47. // Size rounds up to one page.
  48. void* allocation = ::VirtualAlloc(nullptr, 1, MEM_RESERVE | MEM_COMMIT,
  49. PAGE_EXECUTE_READWRITE);
  50. if (!allocation) {
  51. DWORD error = ::GetLastError();
  52. return static_cast<int>(error);
  53. }
  54. ::VirtualFree(allocation, 0, MEM_RELEASE);
  55. break;
  56. }
  57. case VIRTUALPROTECT: {
  58. // Test VirtualProtect with PAGE_EXECUTE_READWRITE.
  59. //-------------------------------------------------
  60. // Use an existing executable function pointer.
  61. BYTE* function = reinterpret_cast<BYTE*>(&DynamicCodeTestHarness);
  62. DWORD old_protect, temp = 0;
  63. // Test making executable binary writable.
  64. if (!::VirtualProtect(function, sizeof(size_t), PAGE_EXECUTE_READWRITE,
  65. &old_protect)) {
  66. DWORD error = ::GetLastError();
  67. return static_cast<int>(error);
  68. }
  69. // Make sure to test the change back to executable.
  70. if (!::VirtualProtect(function, sizeof(size_t), old_protect, &temp)) {
  71. DWORD error = ::GetLastError();
  72. return static_cast<int>(error);
  73. }
  74. break;
  75. }
  76. case MAPVIEWCUSTOM: {
  77. // Test MapViewOfFile with FILE_MAP_EXECUTE | FILE_MAP_WRITE.
  78. // (Custom created mapping.)
  79. //-----------------------------------------------------------
  80. HANDLE section =
  81. ::CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr,
  82. PAGE_EXECUTE_READWRITE, 0, 4096, L"TestMapping");
  83. if (!section) {
  84. DWORD error = ::GetLastError();
  85. return static_cast<int>(error);
  86. }
  87. // Note: this test hinges on FILE_MAP_EXECUTE | FILE_MAP_WRITE access.
  88. // Any other access request will succeed even with the mitigation enabled.
  89. HANDLE* view = reinterpret_cast<HANDLE*>(::MapViewOfFile(
  90. section, FILE_MAP_EXECUTE | FILE_MAP_WRITE, 0, 0, 4096));
  91. if (!view) {
  92. DWORD error = ::GetLastError();
  93. return static_cast<int>(error);
  94. }
  95. ::UnmapViewOfFile(view);
  96. ::CloseHandle(section);
  97. break;
  98. }
  99. case MAPVIEWFILE: {
  100. // Test MapViewOfFile with FILE_MAP_EXECUTE | FILE_MAP_WRITE.
  101. // (Existing file on disk mapping.)
  102. //-----------------------------------------------------------
  103. // Caller should have passed in a non-null file path.
  104. if (!path)
  105. return sandbox::SBOX_TEST_INVALID_PARAMETER;
  106. // Note: INVALID_HANDLE_VALUE
  107. HANDLE file_handle =
  108. ::CreateFile(path, GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE,
  109. FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
  110. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
  111. if (file_handle == INVALID_HANDLE_VALUE) {
  112. DWORD error = ::GetLastError();
  113. return static_cast<int>(error);
  114. }
  115. HANDLE mapping_handle = ::CreateFileMapping(
  116. file_handle, nullptr, PAGE_EXECUTE_READWRITE, 0, 1, nullptr);
  117. if (!mapping_handle) {
  118. ::CloseHandle(file_handle);
  119. DWORD error = ::GetLastError();
  120. return static_cast<int>(error);
  121. }
  122. // Note: this test hinges on FILE_MAP_EXECUTE | FILE_MAP_WRITE access.
  123. // Any other access request will succeed even with the mitigation enabled.
  124. void* view_start = ::MapViewOfFile(
  125. mapping_handle, FILE_MAP_EXECUTE | FILE_MAP_WRITE, 0, 0, 0);
  126. if (!view_start) {
  127. ::CloseHandle(mapping_handle);
  128. ::CloseHandle(file_handle);
  129. DWORD error = ::GetLastError();
  130. return static_cast<int>(error);
  131. }
  132. ::UnmapViewOfFile(view_start);
  133. ::CloseHandle(mapping_handle);
  134. ::CloseHandle(file_handle);
  135. break;
  136. }
  137. default:
  138. return sandbox::SBOX_TEST_INVALID_PARAMETER;
  139. }
  140. return sandbox::SBOX_TEST_SUCCEEDED;
  141. }
  142. // Thread class for testing dynamic code per-thread opt-out.
  143. class DynamicCodeOptOutThread {
  144. public:
  145. // |path| optional, depending on |which_test|.
  146. DynamicCodeOptOutThread(bool mitigation,
  147. DynCodeAPI which_test,
  148. wchar_t* path = nullptr)
  149. : thread_(nullptr),
  150. opt_out_(mitigation),
  151. which_api_test_(which_test),
  152. file_path_(path),
  153. return_code_(sandbox::SBOX_TEST_NOT_FOUND) {}
  154. DynamicCodeOptOutThread(const DynamicCodeOptOutThread&) = delete;
  155. DynamicCodeOptOutThread& operator=(const DynamicCodeOptOutThread&) = delete;
  156. ~DynamicCodeOptOutThread() {
  157. if (thread_) {
  158. ::CloseHandle(thread_);
  159. thread_ = nullptr;
  160. }
  161. }
  162. // LPTHREAD_START_ROUTINE
  163. static DWORD WINAPI StaticThreadFunc(LPVOID lpParam) {
  164. DynamicCodeOptOutThread* this_thread =
  165. reinterpret_cast<DynamicCodeOptOutThread*>(lpParam);
  166. return this_thread->ThreadFunc();
  167. }
  168. // Main function. Call this to create and start the test thread.
  169. // Call Join() to get the test result.
  170. void Start() {
  171. if (thread_)
  172. return;
  173. thread_ = ::CreateThread(nullptr, 0, StaticThreadFunc, this, 0, nullptr);
  174. if (!thread_) {
  175. return_code_ = ::GetLastError();
  176. return;
  177. }
  178. }
  179. // Wait for test thread to finish, and get the final test result.
  180. int Join() {
  181. // Handle case where thread creation failed.
  182. if (!thread_)
  183. return return_code_;
  184. // NOTE: TestTimeouts::action_max_timeout() is not long enough here. In
  185. // debug build this times out.
  186. DWORD timeout = ::IsDebuggerPresent() ? INFINITE : 5000;
  187. return_code_ = ::WaitForSingleObject(thread_, timeout);
  188. // Handle case of abnormal thread exit (or timeout).
  189. if (return_code_ != WAIT_OBJECT_0)
  190. return return_code_;
  191. if (!::GetExitCodeThread(thread_,
  192. reinterpret_cast<DWORD*>(&return_code_))) {
  193. // Handle unexpected case of failing to get thread exit code.
  194. return_code_ = ::GetLastError();
  195. return return_code_;
  196. }
  197. return return_code_;
  198. }
  199. private:
  200. DWORD ThreadFunc() {
  201. // Opt-out this thread from disabled dynamic code.
  202. if (opt_out_) {
  203. if (!sandbox::ApplyMitigationsToCurrentThread(
  204. sandbox::MITIGATION_DYNAMIC_CODE_OPT_OUT_THIS_THREAD)) {
  205. return ::GetLastError();
  206. }
  207. }
  208. // Run the test.
  209. return DynamicCodeTest(which_api_test_, file_path_);
  210. }
  211. HANDLE thread_;
  212. bool opt_out_;
  213. DynCodeAPI which_api_test_;
  214. raw_ptr<wchar_t> file_path_;
  215. int return_code_;
  216. };
  217. // Helpers to set up rules for dynamic code tests, needed as policy
  218. // (from the TestRunner) can only be applied to a single process.
  219. std::unique_ptr<sandbox::TestRunner> RunnerWithMitigation(
  220. sandbox::MitigationFlags mitigations) {
  221. auto runner = std::make_unique<sandbox::TestRunner>();
  222. runner->GetPolicy()->SetDelayedProcessMitigations(mitigations);
  223. return runner;
  224. }
  225. //------------------------------------------------------------------------------
  226. // DisableDynamicCode test harness helper function. Tests numerous APIs.
  227. // - APIs fail with ERROR_DYNAMIC_CODE_BLOCKED if this mitigation is
  228. // enabled and the target tries to meddle.
  229. // - Acquire the global g_hooking_dll_mutex mutex before calling
  230. // (as we meddle with a shared system resource).
  231. // - Note: Do not use ASSERTs in this function, as a global mutex is held.
  232. //
  233. // Trigger test child processes (with or without mitigation enabled).
  234. //------------------------------------------------------------------------------
  235. void DynamicCodeTestHarness(sandbox::MitigationFlags which_mitigation,
  236. bool expect_success,
  237. bool enable_mitigation,
  238. bool with_thread_opt_out) {
  239. if (which_mitigation != sandbox::MITIGATION_DYNAMIC_CODE_DISABLE &&
  240. which_mitigation !=
  241. sandbox::MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT) {
  242. ADD_FAILURE();
  243. return;
  244. }
  245. std::wstring shared =
  246. (which_mitigation == sandbox::MITIGATION_DYNAMIC_CODE_DISABLE)
  247. ? L"TestWin81DynamicCode "
  248. : L"TestWin10DynamicCodeWithOptOut ";
  249. if (which_mitigation ==
  250. sandbox::MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT) {
  251. shared += (with_thread_opt_out) ? L"true" : L"false";
  252. }
  253. // Test 1:
  254. auto runner = enable_mitigation ? RunnerWithMitigation(which_mitigation)
  255. : std::make_unique<sandbox::TestRunner>();
  256. std::wstring test =
  257. base::StringPrintf(L"%ls %u", shared.c_str(), VIRTUALALLOC);
  258. EXPECT_EQ((expect_success ? sandbox::SBOX_TEST_SUCCEEDED
  259. : ERROR_DYNAMIC_CODE_BLOCKED),
  260. runner->RunTest(test.c_str()));
  261. // Test 2:
  262. runner = enable_mitigation ? RunnerWithMitigation(which_mitigation)
  263. : std::make_unique<sandbox::TestRunner>();
  264. test = base::StringPrintf(L"%ls %u", shared.c_str(), VIRTUALPROTECT);
  265. EXPECT_EQ((expect_success ? sandbox::SBOX_TEST_SUCCEEDED
  266. : ERROR_DYNAMIC_CODE_BLOCKED),
  267. runner->RunTest(test.c_str()));
  268. // Test 3:
  269. // Need token level >= USER_LIMITED to be able to successfully run test 3.
  270. runner = enable_mitigation ? RunnerWithMitigation(which_mitigation)
  271. : std::make_unique<sandbox::TestRunner>();
  272. runner->GetPolicy()->SetTokenLevel(
  273. sandbox::TokenLevel::USER_RESTRICTED_SAME_ACCESS,
  274. sandbox::TokenLevel::USER_LIMITED);
  275. test = base::StringPrintf(L"%ls %u", shared.c_str(), MAPVIEWCUSTOM);
  276. EXPECT_EQ((expect_success ? sandbox::SBOX_TEST_SUCCEEDED
  277. : ERROR_DYNAMIC_CODE_BLOCKED),
  278. runner->RunTest(test.c_str()));
  279. // Ensure sandbox access to the file on disk.
  280. base::FilePath dll_path;
  281. ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &dll_path));
  282. dll_path = dll_path.Append(hooking_dll::g_hook_dll_file);
  283. // File must be writable, so create a writable copy in a temporary directory.
  284. base::ScopedTempDir temp_dir;
  285. ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  286. base::FilePath temp_dll_path =
  287. temp_dir.GetPath().Append(hooking_dll::g_hook_dll_file);
  288. ASSERT_TRUE(base::CopyFile(dll_path, temp_dll_path));
  289. runner = enable_mitigation ? RunnerWithMitigation(which_mitigation)
  290. : std::make_unique<sandbox::TestRunner>();
  291. EXPECT_TRUE(runner->AddFsRule(sandbox::Semantics::kFilesAllowAny,
  292. temp_dll_path.value().c_str()));
  293. test = base::StringPrintf(L"%ls %u \"%ls\"", shared.c_str(), MAPVIEWFILE,
  294. temp_dll_path.value().c_str());
  295. EXPECT_EQ((expect_success ? sandbox::SBOX_TEST_SUCCEEDED
  296. : ERROR_DYNAMIC_CODE_BLOCKED),
  297. runner->RunTest(test.c_str()));
  298. }
  299. } // namespace
  300. namespace sandbox {
  301. //------------------------------------------------------------------------------
  302. // Exported functions called by child test processes.
  303. //------------------------------------------------------------------------------
  304. // Parse arguments and do the test.
  305. //
  306. // - Arg1 is a DynCodeAPI indicating which API to test.
  307. // - [OPTIONAL] If Arg1 is MAPVIEWFILE, Arg2 is a file path to map.
  308. SBOX_TESTS_COMMAND int TestWin81DynamicCode(int argc, wchar_t** argv) {
  309. if (argc < 1 || !argv[0])
  310. return SBOX_TEST_INVALID_PARAMETER;
  311. // Arg1
  312. int test = ::_wtoi(argv[0]);
  313. if (test <= 0 || test >= NOTSUPPORTED)
  314. return SBOX_TEST_INVALID_PARAMETER;
  315. // [OPTIONAL] Arg2
  316. wchar_t* path = nullptr;
  317. if (argc > 1)
  318. path = argv[1];
  319. return DynamicCodeTest(static_cast<DynCodeAPI>(test), path);
  320. }
  321. // Parse arguments and spawn the test thread.
  322. //
  323. // - Arg1 is a bool indicating whether to opt-out the test thread.
  324. // - Arg2 is a DynCodeAPI indicating which API to test.
  325. // - [OPTIONAL] If Arg2 is MAPVIEWFILE, Arg3 is a file path to map.
  326. SBOX_TESTS_COMMAND int TestWin10DynamicCodeWithOptOut(int argc,
  327. wchar_t** argv) {
  328. if (argc < 2 || !argv[0] || !argv[1])
  329. return SBOX_TEST_INVALID_PARAMETER;
  330. // Arg1
  331. bool opt_out = false;
  332. if (::wcsicmp(argv[0], L"true") == 0)
  333. opt_out = true;
  334. // Arg2
  335. int test = ::_wtoi(argv[1]);
  336. if (test <= 0 || test >= NOTSUPPORTED)
  337. return SBOX_TEST_INVALID_PARAMETER;
  338. // [OPTIONAL] Arg3
  339. wchar_t* path = nullptr;
  340. if (argc > 2)
  341. path = argv[2];
  342. // Spawn new thread and wait for it to finish!
  343. DynamicCodeOptOutThread opt_out_thread(opt_out, static_cast<DynCodeAPI>(test),
  344. path);
  345. opt_out_thread.Start();
  346. return opt_out_thread.Join();
  347. }
  348. //------------------------------------------------------------------------------
  349. // Exported Dynamic Code Tests
  350. //------------------------------------------------------------------------------
  351. //------------------------------------------------------------------------------
  352. // Disable dynamic code (MITIGATION_DYNAMIC_CODE_DISABLE)
  353. // >= Win8.1
  354. //------------------------------------------------------------------------------
  355. // This test validates that setting the MITIGATION_DYNAMIC_CODE_DISABLE
  356. // mitigation enables the setting on a process.
  357. TEST(ProcessMitigationsTest, CheckWin81DynamicCodePolicySuccess) {
  358. if (base::win::GetVersion() < base::win::Version::WIN8_1)
  359. return;
  360. // TODO(crbug.com/805414): Windows ASan hotpatching requires dynamic code.
  361. #if !defined(ADDRESS_SANITIZER)
  362. std::wstring test_command = L"CheckPolicy ";
  363. test_command += std::to_wstring(TESTPOLICY_DYNAMICCODE);
  364. //---------------------------------
  365. // 1) Test setting pre-startup.
  366. // **Currently only running pre-startup in release. Due to the sandbox in the
  367. // child using dynamic code for hooks, calls to "dynamic code APIs" are
  368. // failing... silently in release, but assert/breakpoint in debug. Since
  369. // this test is only to check the policy setting, ignoring the failures is ok.
  370. //---------------------------------
  371. #if defined(NDEBUG)
  372. TestRunner runner;
  373. sandbox::TargetPolicy* policy = runner.GetPolicy();
  374. EXPECT_EQ(policy->SetProcessMitigations(MITIGATION_DYNAMIC_CODE_DISABLE),
  375. SBOX_ALL_OK);
  376. EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(test_command.c_str()));
  377. #endif // defined(NDEBUG)
  378. //---------------------------------
  379. // 2) Test setting post-startup.
  380. //---------------------------------
  381. TestRunner runner2;
  382. sandbox::TargetPolicy* policy2 = runner2.GetPolicy();
  383. EXPECT_EQ(
  384. policy2->SetDelayedProcessMitigations(MITIGATION_DYNAMIC_CODE_DISABLE),
  385. SBOX_ALL_OK);
  386. EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner2.RunTest(test_command.c_str()));
  387. #endif
  388. }
  389. // This test validates that we can meddle with dynamic code if the
  390. // MITIGATION_DYNAMIC_CODE_DISABLE mitigation is NOT set.
  391. TEST(ProcessMitigationsTest, CheckWin81DynamicCode_BaseCase) {
  392. if (base::win::GetVersion() < base::win::Version::WIN8_1)
  393. return;
  394. ScopedTestMutex mutex(hooking_dll::g_hooking_dll_mutex);
  395. // Expect success, no mitigation.
  396. DynamicCodeTestHarness(sandbox::MITIGATION_DYNAMIC_CODE_DISABLE,
  397. true /* expect_success */,
  398. false /* enable_mitigation */);
  399. }
  400. // This test validates that setting the MITIGATION_DYNAMIC_CODE_DISABLE
  401. // mitigation prevents meddling with dynamic code.
  402. TEST(ProcessMitigationsTest, CheckWin81DynamicCode_TestMitigation) {
  403. if (base::win::GetVersion() < base::win::Version::WIN8_1)
  404. return;
  405. ScopedTestMutex mutex(hooking_dll::g_hooking_dll_mutex);
  406. // Expect failure, with mitigation.
  407. DynamicCodeTestHarness(sandbox::MITIGATION_DYNAMIC_CODE_DISABLE,
  408. false /* expect_success */,
  409. true /* enable_mitigation */);
  410. }
  411. //------------------------------------------------------------------------------
  412. // Disable dynamic code, with per-thread opt-out enabled
  413. // (MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT).
  414. // >= Win10_RS1 (Anniversary)
  415. //------------------------------------------------------------------------------
  416. // This test validates that setting the
  417. // MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT mitigation enables the setting
  418. // on a process.
  419. TEST(ProcessMitigationsTest, CheckWin10DynamicCodeOptOutPolicySuccess) {
  420. if (base::win::GetVersion() < base::win::Version::WIN10_RS1)
  421. return;
  422. // TODO(crbug.com/805414): Windows ASan hotpatching requires dynamic code.
  423. #if !defined(ADDRESS_SANITIZER)
  424. std::wstring test_command = L"CheckPolicy ";
  425. test_command += std::to_wstring(TESTPOLICY_DYNAMICCODEOPTOUT);
  426. //---------------------------------
  427. // 1) Test setting pre-startup.
  428. // **Currently only running pre-startup in release. Due to the sandbox in the
  429. // child using dynamic code for hooks, calls to "dynamic code APIs" are
  430. // failing... silently in release, but assert/breakpoint in debug. Since
  431. // this test is only to check the policy setting, ignoring the failures is ok.
  432. //---------------------------------
  433. #if defined(NDEBUG)
  434. TestRunner runner;
  435. sandbox::TargetPolicy* policy = runner.GetPolicy();
  436. EXPECT_EQ(policy->SetProcessMitigations(
  437. MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT),
  438. SBOX_ALL_OK);
  439. EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(test_command.c_str()));
  440. #endif // defined(NDEBUG)
  441. //---------------------------------
  442. // 2) Test setting post-startup.
  443. //---------------------------------
  444. TestRunner runner2;
  445. sandbox::TargetPolicy* policy2 = runner2.GetPolicy();
  446. EXPECT_EQ(policy2->SetDelayedProcessMitigations(
  447. MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT),
  448. SBOX_ALL_OK);
  449. EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner2.RunTest(test_command.c_str()));
  450. #endif
  451. }
  452. // This test validates that we CAN meddle with dynamic code if the
  453. // MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT mitigation is NOT set.
  454. TEST(ProcessMitigationsTest, CheckWin10DynamicCodeOptOut_BaseCase) {
  455. if (base::win::GetVersion() < base::win::Version::WIN10_RS1)
  456. return;
  457. ScopedTestMutex mutex(hooking_dll::g_hooking_dll_mutex);
  458. // Expect success, no mitigation (and therefore no thread opt-out).
  459. DynamicCodeTestHarness(sandbox::MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT,
  460. true /* expect_success */,
  461. false /* enable_mitigation */,
  462. false /* with_thread_opt_out */);
  463. }
  464. // This test validates that setting the
  465. // MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT mitigation BLOCKS meddling
  466. // with dynamic code.
  467. TEST(ProcessMitigationsTest, CheckWin10DynamicCodeOptOut_TestMitigation) {
  468. if (base::win::GetVersion() < base::win::Version::WIN10_RS1)
  469. return;
  470. ScopedTestMutex mutex(hooking_dll::g_hooking_dll_mutex);
  471. // Expect failure, with mitigation, no thread opt-out.
  472. DynamicCodeTestHarness(sandbox::MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT,
  473. false /* expect_success */,
  474. true /* enable_mitigation */,
  475. false /* with_thread_opt_out */);
  476. }
  477. // This test validates that setting the
  478. // MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT mitigation AND using
  479. // thread-specific opt-out ALLOWS meddling with dynamic code.
  480. TEST(ProcessMitigationsTest,
  481. CheckWin10DynamicCodeOptOut_TestMitigationWithOptOut) {
  482. if (base::win::GetVersion() < base::win::Version::WIN10_RS1)
  483. return;
  484. ScopedTestMutex mutex(hooking_dll::g_hooking_dll_mutex);
  485. // Expect success, with mitigation, with thread opt-out.
  486. DynamicCodeTestHarness(sandbox::MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT,
  487. true /* expect_success */,
  488. true /* enable_mitigation */,
  489. true /* with_thread_opt_out */);
  490. }
  491. } // namespace sandbox