remote_syscall_arg_handler_unittest.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. // Copyright 2020 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/linux/syscall_broker/remote_syscall_arg_handler.h"
  5. #include <sys/mman.h>
  6. #include <sys/types.h>
  7. #include <algorithm>
  8. #include <cstring>
  9. #include <tuple>
  10. #include "base/bind.h"
  11. #include "base/callback_helpers.h"
  12. #include "base/files/scoped_file.h"
  13. #include "base/memory/page_size.h"
  14. #include "base/posix/unix_domain_socket.h"
  15. #include "base/test/bind.h"
  16. #include "sandbox/linux/tests/test_utils.h"
  17. #include "sandbox/linux/tests/unit_tests.h"
  18. #include "testing/gtest/include/gtest/gtest.h"
  19. namespace sandbox {
  20. namespace syscall_broker {
  21. namespace {
  22. const char kPathPart[] = "/i/am/path";
  23. void FillBufferWithPath(char* buf, size_t size, bool null_terminate) {
  24. SANDBOX_ASSERT_LE(size, static_cast<size_t>(PATH_MAX));
  25. size_t str_len = strlen(kPathPart);
  26. size_t len_left_to_write = size;
  27. char* curr_buf_pos = buf;
  28. while (len_left_to_write > 0) {
  29. size_t bytes_to_write = std::min(str_len, len_left_to_write);
  30. memcpy(curr_buf_pos, kPathPart, bytes_to_write);
  31. curr_buf_pos += bytes_to_write;
  32. len_left_to_write -= bytes_to_write;
  33. }
  34. if (null_terminate) {
  35. buf[size - 1] = '\0';
  36. }
  37. }
  38. void VerifyCorrectString(std::string str, size_t size) {
  39. SANDBOX_ASSERT_EQ(str.size(), size);
  40. size_t curr_path_part_pos = 0;
  41. for (char ch : str) {
  42. SANDBOX_ASSERT(ch == kPathPart[curr_path_part_pos]);
  43. curr_path_part_pos++;
  44. curr_path_part_pos %= strlen(kPathPart);
  45. }
  46. }
  47. pid_t ForkWaitingChild(base::OnceCallback<void(int)>
  48. after_parent_signals_callback = base::DoNothing(),
  49. base::ScopedFD* parent_sync_fd = nullptr) {
  50. base::ScopedFD parent_sync, child_sync;
  51. base::CreateSocketPair(&parent_sync, &child_sync);
  52. pid_t pid = fork();
  53. if (!pid) {
  54. parent_sync.reset();
  55. char dummy_char = 'a';
  56. std::vector<base::ScopedFD> empty_fd_vec;
  57. // Wait for parent to exit before exiting ourselves.
  58. base::UnixDomainSocket::RecvMsg(child_sync.get(), &dummy_char, 1,
  59. &empty_fd_vec);
  60. std::move(after_parent_signals_callback).Run(child_sync.get());
  61. _exit(1);
  62. }
  63. child_sync.reset();
  64. if (parent_sync_fd)
  65. *parent_sync_fd = std::move(parent_sync);
  66. else
  67. std::ignore = parent_sync.release(); // Closes when parent dies.
  68. return pid;
  69. }
  70. struct ReadTestConfig {
  71. size_t start_at = 0;
  72. size_t total_size = strlen(kPathPart) + 1;
  73. bool include_null_byte = true;
  74. bool last_page_inaccessible = false;
  75. RemoteProcessIOResult result = RemoteProcessIOResult::kSuccess;
  76. };
  77. void ReadTest(const ReadTestConfig& test_config) {
  78. // Map exactly the right number of pages for the config parameters.
  79. size_t total_pages = (test_config.start_at + test_config.total_size +
  80. base::GetPageSize() - 1) /
  81. base::GetPageSize();
  82. char* mmap_addr = static_cast<char*>(TestUtils::MapPagesOrDie(total_pages));
  83. char* addr = mmap_addr + test_config.start_at;
  84. FillBufferWithPath(addr, test_config.total_size,
  85. test_config.include_null_byte);
  86. if (test_config.last_page_inaccessible)
  87. TestUtils::MprotectLastPageOrDie(mmap_addr, total_pages);
  88. pid_t pid = ForkWaitingChild();
  89. munmap(mmap_addr, base::GetPageSize() * total_pages);
  90. std::string out_str;
  91. SANDBOX_ASSERT_EQ(ReadFilePathFromRemoteProcess(pid, addr, &out_str),
  92. test_config.result);
  93. if (test_config.result == RemoteProcessIOResult::kSuccess) {
  94. VerifyCorrectString(std::move(out_str), test_config.total_size - 1);
  95. }
  96. }
  97. } // namespace
  98. // | path + null_byte |
  99. SANDBOX_TEST(BrokerRemoteSyscallArgHandler, BasicRead) {
  100. ReadTest(ReadTestConfig());
  101. }
  102. // | zero + path... | ...path + null_byte + zero |
  103. SANDBOX_TEST(BrokerRemoteSyscallArgHandler, MultipageRead) {
  104. ReadTestConfig config;
  105. CHECK(PATH_MAX / 2 <= base::GetPageSize());
  106. config.start_at = base::GetPageSize() - (PATH_MAX / 2);
  107. config.total_size = PATH_MAX;
  108. ReadTest(config);
  109. }
  110. // | path... | ...path |
  111. SANDBOX_TEST_ALLOW_NOISE(BrokerRemoteSyscallArgHandler, ReadExceededPathMax) {
  112. ReadTestConfig config;
  113. config.total_size = PATH_MAX * 2;
  114. config.result = RemoteProcessIOResult::kExceededPathMax;
  115. }
  116. // | path... | null_byte + zero |
  117. SANDBOX_TEST_ALLOW_NOISE(BrokerRemoteSyscallArgHandler,
  118. ReadBarelyExceededPathMax) {
  119. ReadTestConfig config;
  120. config.total_size = PATH_MAX + 1;
  121. config.result = RemoteProcessIOResult::kExceededPathMax;
  122. }
  123. // | zero + path... | INACCESSIBLE |
  124. SANDBOX_TEST(BrokerRemoteSyscallArgHandler, ReadUnreadablePage) {
  125. ReadTestConfig config;
  126. config.start_at = base::GetPageSize() - (PATH_MAX / 2);
  127. config.total_size = PATH_MAX / 2;
  128. config.last_page_inaccessible = true;
  129. config.include_null_byte = false;
  130. config.result = RemoteProcessIOResult::kRemoteMemoryInvalid;
  131. ReadTest(config);
  132. }
  133. SANDBOX_TEST(BrokerRemoteSyscallArgHandler, ReadChunkMinus1) {
  134. ReadTestConfig config;
  135. config.total_size = internal::kNumBytesPerChunk - 1;
  136. ReadTest(config);
  137. }
  138. SANDBOX_TEST(BrokerRemoteSyscallArgHandler, ReadChunk) {
  139. ReadTestConfig config;
  140. config.total_size = internal::kNumBytesPerChunk;
  141. ReadTest(config);
  142. }
  143. SANDBOX_TEST(BrokerRemoteSyscallArgHandler, ReadChunkPlus1) {
  144. ReadTestConfig config;
  145. config.total_size = internal::kNumBytesPerChunk + 1;
  146. ReadTest(config);
  147. }
  148. SANDBOX_TEST(BrokerRemoteSyscallArgHandler, ReadChunkEndingAtPage) {
  149. ReadTestConfig config;
  150. config.start_at = base::GetPageSize() - internal::kNumBytesPerChunk;
  151. config.total_size = internal::kNumBytesPerChunk;
  152. ReadTest(config);
  153. }
  154. SANDBOX_TEST(BrokerRemoteSyscallArgHandler, ReadChunkEndingOnePastPage) {
  155. ReadTestConfig config;
  156. config.start_at = base::GetPageSize() - internal::kNumBytesPerChunk + 1;
  157. config.total_size = internal::kNumBytesPerChunk;
  158. ReadTest(config);
  159. }
  160. SANDBOX_TEST(BrokerRemoteSyscallArgHandler, ReadChunkPlus1EndingOnePastPage) {
  161. ReadTestConfig config;
  162. config.start_at = base::GetPageSize() - internal::kNumBytesPerChunk;
  163. config.total_size = internal::kNumBytesPerChunk + 1;
  164. ReadTest(config);
  165. }
  166. SANDBOX_TEST(BrokerRemoteSyscallArgHandler, ReadChildExited) {
  167. void* addr = TestUtils::MapPagesOrDie(1);
  168. FillBufferWithPath(static_cast<char*>(addr), strlen(kPathPart) + 1, true);
  169. base::ScopedFD parent_sync, child_sync;
  170. base::CreateSocketPair(&parent_sync, &child_sync);
  171. pid_t pid = fork();
  172. if (!pid) {
  173. parent_sync.reset();
  174. _exit(1);
  175. }
  176. child_sync.reset();
  177. // Wait for child to exit before reading memory.
  178. char dummy_char = 'a';
  179. std::vector<base::ScopedFD> empty_fd_vec;
  180. base::UnixDomainSocket::RecvMsg(parent_sync.get(), &dummy_char, 1,
  181. &empty_fd_vec);
  182. munmap(addr, base::GetPageSize());
  183. std::string out_str;
  184. SANDBOX_ASSERT_EQ(ReadFilePathFromRemoteProcess(pid, addr, &out_str),
  185. RemoteProcessIOResult::kRemoteExited);
  186. }
  187. SANDBOX_TEST(BrokerRemoteSyscallArgHandler, BasicWrite) {
  188. void* read_from = TestUtils::MapPagesOrDie(1);
  189. const size_t write_size = base::GetPageSize();
  190. FillBufferWithPath(static_cast<char*>(read_from), write_size, false);
  191. char* write_to = static_cast<char*>(TestUtils::MapPagesOrDie(1));
  192. base::ScopedFD parent_signal_fd;
  193. const std::vector<int> empty_fd_vec;
  194. pid_t pid =
  195. ForkWaitingChild(base::BindLambdaForTesting([=](int child_sync_fd) {
  196. // Check correct result received and tell parent about
  197. // success.
  198. int res = memcmp(read_from, write_to, write_size);
  199. base::UnixDomainSocket::SendMsg(
  200. child_sync_fd, &res, sizeof(res), empty_fd_vec);
  201. _exit(1);
  202. }),
  203. &parent_signal_fd);
  204. RemoteProcessIOResult result = WriteRemoteData(
  205. pid, reinterpret_cast<uintptr_t>(write_to), write_size,
  206. base::span<char>(static_cast<char*>(read_from), write_size));
  207. SANDBOX_ASSERT_EQ(result, RemoteProcessIOResult::kSuccess);
  208. // Release child.
  209. char dummy_char = 'a';
  210. base::UnixDomainSocket::SendMsg(parent_signal_fd.get(), &dummy_char, 1,
  211. empty_fd_vec);
  212. // Read result of memcmp and assert.
  213. int memcmp_res;
  214. std::vector<base::ScopedFD> dummy_fd_vec;
  215. base::UnixDomainSocket::RecvMsg(parent_signal_fd.get(), &memcmp_res,
  216. sizeof(memcmp_res), &dummy_fd_vec);
  217. SANDBOX_ASSERT_EQ(memcmp_res, 0);
  218. }
  219. SANDBOX_TEST(BrokerRemoteSyscallArgHandler, WriteToInvalidAddress) {
  220. char* write_to = static_cast<char*>(TestUtils::MapPagesOrDie(1));
  221. TestUtils::MprotectLastPageOrDie(write_to, 1);
  222. base::ScopedFD parent_signal_fd;
  223. const std::vector<int> empty_fd_vec;
  224. pid_t pid = ForkWaitingChild();
  225. munmap(write_to, base::GetPageSize());
  226. char buf[5];
  227. memset(buf, 'a', sizeof(buf));
  228. RemoteProcessIOResult result =
  229. WriteRemoteData(pid, reinterpret_cast<uintptr_t>(write_to), sizeof(buf),
  230. base::span<char>(buf, sizeof(buf)));
  231. SANDBOX_ASSERT_EQ(result, RemoteProcessIOResult::kRemoteMemoryInvalid);
  232. }
  233. SANDBOX_TEST(BrokerRemoteSyscallArgHandler, WritePartiallyToInvalidAddress) {
  234. char* read_from = static_cast<char*>(TestUtils::MapPagesOrDie(2));
  235. const size_t write_size = base::GetPageSize();
  236. FillBufferWithPath(static_cast<char*>(read_from), write_size, false);
  237. char* write_to = static_cast<char*>(TestUtils::MapPagesOrDie(2));
  238. TestUtils::MprotectLastPageOrDie(write_to, 2);
  239. write_to += base::GetPageSize() / 2;
  240. base::ScopedFD parent_signal_fd;
  241. const std::vector<int> empty_fd_vec;
  242. pid_t pid = ForkWaitingChild();
  243. munmap(write_to, base::GetPageSize());
  244. RemoteProcessIOResult result =
  245. WriteRemoteData(pid, reinterpret_cast<uintptr_t>(write_to), write_size,
  246. base::span<char>(read_from, write_size));
  247. SANDBOX_ASSERT_EQ(result, RemoteProcessIOResult::kRemoteMemoryInvalid);
  248. }
  249. SANDBOX_TEST(BrokerRemoteSyscallArgHandler, WriteChildExited) {
  250. char* addr = static_cast<char*>(TestUtils::MapPagesOrDie(1));
  251. FillBufferWithPath(static_cast<char*>(addr), strlen(kPathPart) + 1, true);
  252. base::ScopedFD parent_sync, child_sync;
  253. base::CreateSocketPair(&parent_sync, &child_sync);
  254. pid_t pid = fork();
  255. if (!pid) {
  256. parent_sync.reset();
  257. _exit(1);
  258. }
  259. child_sync.reset();
  260. // Wait for child to exit before writing memory.
  261. char dummy_char = 'a';
  262. std::vector<base::ScopedFD> empty_fd_vec;
  263. base::UnixDomainSocket::RecvMsg(parent_sync.get(), &dummy_char, 1,
  264. &empty_fd_vec);
  265. std::string out_str;
  266. SANDBOX_ASSERT_EQ(
  267. WriteRemoteData(pid, reinterpret_cast<uintptr_t>(addr), strlen(kPathPart),
  268. base::span<char>(addr, strlen(kPathPart))),
  269. RemoteProcessIOResult::kRemoteExited);
  270. }
  271. } // namespace syscall_broker
  272. } // namespace sandbox