sandbox_nt_util_unittest.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. // Copyright 2015 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 "sandbox/win/src/sandbox_nt_util.h"
  5. #include <windows.h>
  6. #include <memory>
  7. #include <vector>
  8. #include "base/files/file.h"
  9. #include "base/path_service.h"
  10. #include "base/strings/string_util.h"
  11. #include "base/win/scoped_handle.h"
  12. #include "base/win/scoped_process_information.h"
  13. #include "sandbox/win/src/policy_broker.h"
  14. #include "sandbox/win/src/win_utils.h"
  15. #include "testing/gtest/include/gtest/gtest.h"
  16. namespace sandbox {
  17. namespace {
  18. TEST(SandboxNtUtil, IsSameProcessPseudoHandle) {
  19. HANDLE current_process_pseudo = GetCurrentProcess();
  20. EXPECT_TRUE(IsSameProcess(current_process_pseudo));
  21. }
  22. TEST(SandboxNtUtil, IsSameProcessNonPseudoHandle) {
  23. base::win::ScopedHandle current_process(
  24. OpenProcess(PROCESS_QUERY_INFORMATION, false, GetCurrentProcessId()));
  25. ASSERT_TRUE(current_process.IsValid());
  26. EXPECT_TRUE(IsSameProcess(current_process.Get()));
  27. }
  28. TEST(SandboxNtUtil, IsSameProcessDifferentProcess) {
  29. STARTUPINFO si = {sizeof(si)};
  30. PROCESS_INFORMATION pi = {};
  31. wchar_t notepad[] = L"notepad";
  32. ASSERT_TRUE(CreateProcessW(nullptr, notepad, nullptr, nullptr, false, 0,
  33. nullptr, nullptr, &si, &pi));
  34. base::win::ScopedProcessInformation process_info(pi);
  35. EXPECT_FALSE(IsSameProcess(process_info.process_handle()));
  36. EXPECT_TRUE(TerminateProcess(process_info.process_handle(), 0));
  37. }
  38. struct VirtualMemDeleter {
  39. void operator()(char* p) { ::VirtualFree(p, 0, MEM_RELEASE); }
  40. };
  41. typedef std::unique_ptr<char, VirtualMemDeleter> unique_ptr_vmem;
  42. #if defined(_WIN64)
  43. void AllocateBlock(SIZE_T size,
  44. SIZE_T free_size,
  45. char** base_address,
  46. std::vector<unique_ptr_vmem>* mem_range) {
  47. unique_ptr_vmem ptr(static_cast<char*>(::VirtualAlloc(
  48. *base_address, size - free_size, MEM_RESERVE, PAGE_READWRITE)));
  49. ASSERT_NE(nullptr, ptr.get());
  50. mem_range->push_back(std::move(ptr));
  51. *base_address += size;
  52. }
  53. #define KIB(x) ((x)*1024ULL)
  54. #define MIB(x) (KIB(x) * 1024ULL)
  55. #define GIB(x) (MIB(x) * 1024ULL)
  56. // Construct a basic memory layout to do the test. We reserve first to get a
  57. // base address then reallocate with the following pattern.
  58. // |512MiB-64KiB Free|512MiB-128Kib Free|512MiB-256Kib Free|512MiB+512KiB Free|
  59. // The purpose of this is leave a couple of free memory regions within a 2GiB
  60. // block of reserved memory that we can test the searching allocator.
  61. void AllocateTestRange(std::vector<unique_ptr_vmem>* mem_range) {
  62. // Ensure we preallocate enough space in the vector to prevent unexpected
  63. // allocations.
  64. mem_range->reserve(5);
  65. SIZE_T total_size =
  66. MIB(512) + MIB(512) + MIB(512) + MIB(512) + KIB(512) + KIB(64);
  67. unique_ptr_vmem ptr(static_cast<char*>(
  68. ::VirtualAlloc(nullptr, total_size, MEM_RESERVE, PAGE_READWRITE)));
  69. ASSERT_NE(nullptr, ptr.get());
  70. char* base_address = ptr.get();
  71. char* orig_base = base_address;
  72. ptr.reset();
  73. AllocateBlock(MIB(512), KIB(64), &base_address, mem_range);
  74. AllocateBlock(MIB(512), KIB(128), &base_address, mem_range);
  75. AllocateBlock(MIB(512), KIB(256), &base_address, mem_range);
  76. AllocateBlock(MIB(512) + KIB(512), KIB(512), &base_address, mem_range);
  77. // Allocate a memory block at end to act as an upper bound.
  78. AllocateBlock(KIB(64), 0, &base_address, mem_range);
  79. ASSERT_EQ(total_size, static_cast<SIZE_T>(base_address - orig_base));
  80. }
  81. // Test we can allocate appropriate blocks.
  82. void TestAlignedRange(char* base_address) {
  83. unique_ptr_vmem ptr_256k(new (sandbox::NT_PAGE, base_address) char[KIB(256)]);
  84. EXPECT_EQ(base_address + GIB(1) + MIB(512) - KIB(256), ptr_256k.get());
  85. unique_ptr_vmem ptr_64k(new (sandbox::NT_PAGE, base_address) char[KIB(64)]);
  86. EXPECT_EQ(base_address + MIB(512) - KIB(64), ptr_64k.get());
  87. unique_ptr_vmem ptr_128k(new (sandbox::NT_PAGE, base_address) char[KIB(128)]);
  88. EXPECT_EQ(base_address + GIB(1) - KIB(128), ptr_128k.get());
  89. // We will have run out of space here so should also fail.
  90. unique_ptr_vmem ptr_64k_noalloc(
  91. new (sandbox::NT_PAGE, base_address) char[KIB(64)]);
  92. EXPECT_EQ(nullptr, ptr_64k_noalloc.get());
  93. }
  94. // Test the 512k block which exists at the end of the maximum allocation
  95. // boundary.
  96. void Test512kBlock(char* base_address) {
  97. // This should fail as it'll just be out of range.
  98. unique_ptr_vmem ptr_512k_noalloc(
  99. new (sandbox::NT_PAGE, base_address) char[KIB(512)]);
  100. EXPECT_EQ(nullptr, ptr_512k_noalloc.get());
  101. // Check that moving base address we can allocate the 512k block.
  102. unique_ptr_vmem ptr_512k(
  103. new (sandbox::NT_PAGE, base_address + GIB(1)) char[KIB(512)]);
  104. EXPECT_EQ(base_address + GIB(2), ptr_512k.get());
  105. // Free pointer first.
  106. ptr_512k.reset();
  107. ptr_512k.reset(new (sandbox::NT_PAGE, base_address + GIB(2)) char[KIB(512)]);
  108. EXPECT_EQ(base_address + GIB(2), ptr_512k.get());
  109. }
  110. // Test we can allocate appropriate blocks even when starting at an unaligned
  111. // address.
  112. void TestUnalignedRange(char* base_address) {
  113. char* unaligned_base = base_address + 123456;
  114. unique_ptr_vmem ptr_256k(
  115. new (sandbox::NT_PAGE, unaligned_base) char[KIB(256)]);
  116. EXPECT_EQ(base_address + GIB(1) + MIB(512) - KIB(256), ptr_256k.get());
  117. unique_ptr_vmem ptr_64k(new (sandbox::NT_PAGE, unaligned_base) char[KIB(64)]);
  118. EXPECT_EQ(base_address + MIB(512) - KIB(64), ptr_64k.get());
  119. unique_ptr_vmem ptr_128k(
  120. new (sandbox::NT_PAGE, unaligned_base) char[KIB(128)]);
  121. EXPECT_EQ(base_address + GIB(1) - KIB(128), ptr_128k.get());
  122. }
  123. // Test maximum number of available allocations within the predefined pattern.
  124. void TestMaxAllocations(char* base_address) {
  125. // There's only 7 64k blocks in the first 2g which we can fill.
  126. unique_ptr_vmem ptr_1(new (sandbox::NT_PAGE, base_address) char[1]);
  127. EXPECT_NE(nullptr, ptr_1.get());
  128. unique_ptr_vmem ptr_2(new (sandbox::NT_PAGE, base_address) char[1]);
  129. EXPECT_NE(nullptr, ptr_2.get());
  130. unique_ptr_vmem ptr_3(new (sandbox::NT_PAGE, base_address) char[1]);
  131. EXPECT_NE(nullptr, ptr_3.get());
  132. unique_ptr_vmem ptr_4(new (sandbox::NT_PAGE, base_address) char[1]);
  133. EXPECT_NE(nullptr, ptr_4.get());
  134. unique_ptr_vmem ptr_5(new (sandbox::NT_PAGE, base_address) char[1]);
  135. EXPECT_NE(nullptr, ptr_5.get());
  136. unique_ptr_vmem ptr_6(new (sandbox::NT_PAGE, base_address) char[1]);
  137. EXPECT_NE(nullptr, ptr_6.get());
  138. unique_ptr_vmem ptr_7(new (sandbox::NT_PAGE, base_address) char[1]);
  139. EXPECT_NE(nullptr, ptr_7.get());
  140. unique_ptr_vmem ptr_8(new (sandbox::NT_PAGE, base_address) char[1]);
  141. EXPECT_EQ(nullptr, ptr_8.get());
  142. }
  143. // Test extreme allocations we know should fail.
  144. void TestExtremes() {
  145. unique_ptr_vmem ptr_null(new (sandbox::NT_PAGE, nullptr) char[1]);
  146. EXPECT_EQ(nullptr, ptr_null.get());
  147. unique_ptr_vmem ptr_too_large(
  148. new (sandbox::NT_PAGE, reinterpret_cast<void*>(0x1000000)) char[GIB(4)]);
  149. EXPECT_EQ(nullptr, ptr_too_large.get());
  150. unique_ptr_vmem ptr_overflow(
  151. new (sandbox::NT_PAGE, reinterpret_cast<void*>(SIZE_MAX)) char[1]);
  152. EXPECT_EQ(nullptr, ptr_overflow.get());
  153. unique_ptr_vmem ptr_invalid(new (
  154. sandbox::NT_PAGE, reinterpret_cast<void*>(SIZE_MAX - 0x1000000)) char[1]);
  155. EXPECT_EQ(nullptr, ptr_invalid.get());
  156. }
  157. // Test nearest allocator, only do this for 64 bit. We test through the exposed
  158. // new operator as we can't call the AllocateNearTo function directly.
  159. TEST(SandboxNtUtil, NearestAllocator) {
  160. std::vector<unique_ptr_vmem> mem_range;
  161. AllocateTestRange(&mem_range);
  162. ASSERT_LT(0U, mem_range.size());
  163. char* base_address = static_cast<char*>(mem_range[0].get());
  164. TestAlignedRange(base_address);
  165. Test512kBlock(base_address);
  166. TestUnalignedRange(base_address);
  167. TestMaxAllocations(base_address);
  168. TestExtremes();
  169. }
  170. #endif // defined(_WIN64)
  171. // Test whether function ValidParameter works as expected, that is properly
  172. // checks access to the buffer and doesn't modify it in any way.
  173. TEST(SandboxNtUtil, ValidParameter) {
  174. static constexpr unsigned int buffer_size = 4096;
  175. unique_ptr_vmem buffer_guard(static_cast<char*>(
  176. ::VirtualAlloc(nullptr, buffer_size, MEM_COMMIT, PAGE_READWRITE)));
  177. ASSERT_NE(nullptr, buffer_guard.get());
  178. unsigned char* ptr = reinterpret_cast<unsigned char*>(buffer_guard.get());
  179. // Fill the buffer with some data.
  180. for (unsigned int i = 0; i < buffer_size; i++)
  181. ptr[i] = (i % 256);
  182. // Setup verify function.
  183. auto verify_buffer = [&]() {
  184. for (unsigned int i = 0; i < buffer_size; i++) {
  185. if (ptr[i] != (i % 256))
  186. return false;
  187. }
  188. return true;
  189. };
  190. // Verify that the buffer can be written to and doesn't change.
  191. EXPECT_TRUE(ValidParameter(ptr, buffer_size, RequiredAccess::WRITE));
  192. EXPECT_TRUE(verify_buffer());
  193. DWORD old_protection;
  194. // Change the protection of buffer to READONLY.
  195. EXPECT_TRUE(
  196. ::VirtualProtect(ptr, buffer_size, PAGE_READONLY, &old_protection));
  197. // Writting to buffer should fail now.
  198. EXPECT_FALSE(ValidParameter(ptr, buffer_size, RequiredAccess::WRITE));
  199. // But reading should be ok.
  200. EXPECT_TRUE(ValidParameter(ptr, buffer_size, RequiredAccess::READ));
  201. // One final check that the buffer hasn't been modified.
  202. EXPECT_TRUE(verify_buffer());
  203. }
  204. TEST(SandboxNtUtil, NtGetPathFromHandle) {
  205. base::FilePath exe;
  206. ASSERT_TRUE(base::PathService::Get(base::FILE_EXE, &exe));
  207. base::File exe_file(exe, base::File::FLAG_OPEN);
  208. ASSERT_TRUE(exe_file.IsValid());
  209. std::unique_ptr<wchar_t, NtAllocDeleter> path;
  210. EXPECT_TRUE(NtGetPathFromHandle(exe_file.GetPlatformFile(), &path));
  211. // Basic sanity test, the functionality of NtGetPathFromHandle to return
  212. // the correct value is already tested from win_utils_unittest.cc.
  213. EXPECT_TRUE(base::EndsWith(base::AsStringPiece16(path.get()),
  214. base::AsStringPiece16(exe.BaseName().value()),
  215. base::CompareCase::INSENSITIVE_ASCII));
  216. // Compare to GetNtPathFromWin32Path for extra check.
  217. std::wstring nt_path;
  218. EXPECT_TRUE(GetNtPathFromWin32Path(exe.value(), &nt_path));
  219. EXPECT_STREQ(path.get(), nt_path.c_str());
  220. }
  221. TEST(SandboxNtUtil, CopyNameAndAttributes) {
  222. OBJECT_ATTRIBUTES object_attributes;
  223. InitializeObjectAttributes(&object_attributes, nullptr, 0, nullptr, nullptr);
  224. std::unique_ptr<wchar_t, NtAllocDeleter> name;
  225. size_t name_len;
  226. uint32_t attributes;
  227. EXPECT_EQ(STATUS_UNSUCCESSFUL,
  228. sandbox::CopyNameAndAttributes(&object_attributes, &name, &name_len,
  229. &attributes));
  230. UNICODE_STRING object_name = {};
  231. InitializeObjectAttributes(&object_attributes, &object_name, 0,
  232. reinterpret_cast<HANDLE>(0x88), nullptr);
  233. EXPECT_EQ(STATUS_UNSUCCESSFUL,
  234. sandbox::CopyNameAndAttributes(&object_attributes, &name, &name_len,
  235. &attributes));
  236. wchar_t name_buffer[] = {L'A', L'B', L'C', L'D'};
  237. object_name.Length = static_cast<USHORT>(sizeof(name_buffer));
  238. object_name.MaximumLength = object_name.Length;
  239. object_name.Buffer = name_buffer;
  240. InitializeObjectAttributes(&object_attributes, &object_name, 0,
  241. reinterpret_cast<HANDLE>(0x88), nullptr);
  242. EXPECT_EQ(STATUS_UNSUCCESSFUL,
  243. sandbox::CopyNameAndAttributes(&object_attributes, &name, &name_len,
  244. &attributes));
  245. InitializeObjectAttributes(&object_attributes, &object_name, 0x12345678,
  246. nullptr, nullptr);
  247. ASSERT_EQ(STATUS_SUCCESS,
  248. sandbox::CopyNameAndAttributes(&object_attributes, &name, &name_len,
  249. &attributes));
  250. EXPECT_EQ(object_attributes.Attributes, attributes);
  251. EXPECT_EQ(std::size(name_buffer), name_len);
  252. EXPECT_EQ(0, wcsncmp(name.get(), name_buffer, std::size(name_buffer)));
  253. EXPECT_EQ(L'\0', name.get()[name_len]);
  254. }
  255. TEST(SandboxNtUtil, GetNtExports) {
  256. const NtExports* exports = GetNtExports();
  257. ASSERT_TRUE(exports);
  258. static_assert((sizeof(NtExports) % sizeof(void*)) == 0);
  259. // Verify that the structure is fully initialized.
  260. for (size_t i = 0; i < sizeof(NtExports) / sizeof(void*); i++)
  261. EXPECT_TRUE(reinterpret_cast<void* const*>(exports)[i]);
  262. }
  263. } // namespace
  264. } // namespace sandbox