gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/process_vm_read_write.cc (about) 1 // Copyright 2022 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <asm-generic/errno-base.h> 16 #include <bits/types/siginfo_t.h> 17 #include <bits/types/struct_iovec.h> 18 #include <errno.h> 19 #include <linux/futex.h> 20 #include <string.h> 21 #include <sys/types.h> 22 #include <sys/uio.h> 23 #include <sys/wait.h> 24 #include <unistd.h> 25 26 #include <algorithm> 27 #include <atomic> 28 #include <climits> 29 #include <csignal> 30 #include <cstddef> 31 #include <cstdint> 32 #include <functional> 33 #include <iostream> 34 #include <memory> 35 #include <ostream> 36 #include <string> 37 #include <vector> 38 39 #include "gmock/gmock.h" 40 #include "gtest/gtest.h" 41 #include "absl/cleanup/cleanup.h" 42 #include "absl/strings/str_cat.h" 43 #include "absl/strings/str_join.h" 44 #include "test/util/cleanup.h" 45 #include "test/util/logging.h" 46 #include "test/util/memory_util.h" 47 #include "test/util/posix_error.h" 48 #include "test/util/test_util.h" 49 #include "test/util/thread_util.h" 50 51 namespace gvisor { 52 namespace testing { 53 54 namespace { 55 56 class TestIovecs { 57 public: 58 TestIovecs(std::vector<std::string> data) { 59 data_.resize(data.size()); 60 iovecs_.resize(data.size()); 61 for (size_t i = 0; i < data.size(); ++i) { 62 data_[i] = data[i]; 63 bytes_ += data[i].size(); 64 iovecs_[i].iov_len = data_[i].size(); 65 iovecs_[i].iov_base = data_[i].data(); 66 } 67 } 68 69 void erase() { 70 for (size_t i = 0; i < data_.size(); i++) { 71 data_[i].clear(); 72 } 73 } 74 75 // Backing data that will be read/written. 76 std::vector<std::string> data_; 77 78 // Total size of data_. 79 ssize_t bytes_ = 0; 80 81 // Iovec structs that point into data_ 82 std::vector<iovec> iovecs_; 83 }; 84 85 // bytes_match checks that the two TestIovecs are at least min_bytes in length, 86 // and that they agree in the first min_bytes. 87 bool bytes_match(TestIovecs first_iov, TestIovecs second_iov, 88 size_t min_bytes) { 89 auto first = absl::StrJoin(first_iov.data_, ""); 90 if (first.size() < min_bytes) { 91 std::cout << "First buffer smaller than min_bytes: " << min_bytes 92 << " buffer: " << first << std::endl; 93 return false; 94 } 95 first = first.substr(0, min_bytes); 96 97 auto second = absl::StrJoin(second_iov.data_, ""); 98 if (second.size() < min_bytes) { 99 std::cout << "First buffer smaller than min_bytes: " << min_bytes 100 << " buffer: " << second << std::endl; 101 return false; 102 } 103 second = second.substr(0, min_bytes); 104 105 if (first != second) { 106 std::cout << "Mismatch buffers:\n first: " << first 107 << "\n second: " << second << std::endl; 108 return false; 109 } 110 111 return true; 112 } 113 114 struct ProcessVMTestCase { 115 std::string test_name; 116 std::vector<std::string> local_data; 117 std::vector<std::string> remote_data; 118 }; 119 120 using ProcessVMTest = ::testing::TestWithParam<ProcessVMTestCase>; 121 122 std::string getTestBuffer(std::string pattern, size_t size) { 123 std::string s; 124 125 auto pattern_length = pattern.length(); 126 s.reserve(size); 127 while (s.length() + pattern_length < size) { 128 s += pattern; 129 } 130 s += pattern.substr(0, size - s.length()); 131 return s; 132 } 133 134 INSTANTIATE_TEST_SUITE_P( 135 ProcessVMTests, ProcessVMTest, 136 ::testing::ValuesIn<ProcessVMTestCase>( 137 {{"BothEmpty" /*test name*/, 138 {""} /*local buffer*/, 139 {""} /*remote buffer*/}, 140 {"EmptyLocal", {""}, {"All too easy."}}, 141 {"EmptyRemote", {"Impressive. Most impressive."}, {""}}, 142 {"SingleChar", {"l"}, {"r"}}, 143 {"LargerRemoteBuffer", 144 {"OK, I'll try"}, 145 {"No!", "Try not", "Do...or do not", "There is no try."}}, 146 {"LargerLocalBuffer", 147 {"Look!", "The cave is collapsing!"}, 148 {"This is no cave."}}, 149 {"BothWithMultipleIovecs", 150 {"Obi-wan never told you what happened to your father.", 151 "He told me enough...he told me you killed him."}, 152 {"No...I am your father.", "No. No.", "That's not true.", 153 "That's impossible!"}}, 154 { 155 "LargeBuffer", 156 { 157 getTestBuffer( 158 "Train yourself to let go of everything you fear to lose.", 159 32 << 20), 160 "Hello there!", 161 }, 162 { 163 "Do. Or do not. There is no try.", 164 getTestBuffer("The greatest teacher, failure is.", 32 << 20), 165 }, 166 }}), 167 [](const ::testing::TestParamInfo<ProcessVMTest::ParamType>& info) { 168 return info.param.test_name; 169 }); 170 171 // TestReadvSameProcess calls process_vm_readv in the same process with various 172 // local/remote buffers. 173 TEST_P(ProcessVMTest, TestReadvSameProcess) { 174 TestIovecs local(GetParam().local_data); 175 TestIovecs remote(GetParam().remote_data); 176 177 auto want_size = std::min(remote.bytes_, local.bytes_); 178 EXPECT_THAT( 179 process_vm_readv(getpid(), local.iovecs_.data(), local.iovecs_.size(), 180 remote.iovecs_.data(), remote.iovecs_.size(), 0), 181 SyscallSucceedsWithValue(want_size)); 182 EXPECT_TRUE(bytes_match(local, remote, want_size)); 183 } 184 185 // TestReadvSameProcessDifferentThread calls process_vm_readv in the same 186 // process, but with a different (non-leader) remote thread, with various 187 // local/remote buffers. 188 TEST_P(ProcessVMTest, TestReadvSameProcessDifferentThread) { 189 TestIovecs local(GetParam().local_data); 190 TestIovecs remote(GetParam().remote_data); 191 192 std::atomic<std::int32_t> sibling_tid{0}; 193 std::atomic<std::uint32_t> sibling_exit{0}; 194 ScopedThread t([&] { 195 sibling_tid.store(gettid()); 196 syscall(SYS_futex, &sibling_tid, FUTEX_WAKE, 1); 197 while (sibling_exit.load() == 0) { 198 syscall(SYS_futex, &sibling_exit, FUTEX_WAIT, 0, nullptr); 199 } 200 }); 201 Cleanup cleanup_t([&] { 202 sibling_exit.store(1); 203 syscall(SYS_futex, &sibling_exit, FUTEX_WAKE, 1); 204 }); 205 while (sibling_tid.load() == 0) { 206 syscall(SYS_futex, &sibling_tid, FUTEX_WAIT, 0, nullptr); 207 } 208 209 auto want_size = std::min(remote.bytes_, local.bytes_); 210 EXPECT_THAT(process_vm_readv(sibling_tid.load(), local.iovecs_.data(), 211 local.iovecs_.size(), remote.iovecs_.data(), 212 remote.iovecs_.size(), 0), 213 SyscallSucceedsWithValue(want_size)); 214 EXPECT_TRUE(bytes_match(local, remote, want_size)); 215 } 216 217 // TestReadvSubProcess reads data from a forked child process. 218 TEST_P(ProcessVMTest, TestReadvSubProcess) { 219 TestIovecs local = TestIovecs(GetParam().local_data); 220 TestIovecs remote = TestIovecs(GetParam().remote_data); 221 222 pid_t pid = fork(); 223 TEST_CHECK_SUCCESS(pid); 224 if (pid == 0) { 225 // Child. This is the "remote" process. 226 // Wait for parent to read data. 227 sleep(10); // NOLINT - SleepFor is not async-signal-safe. 228 _exit(0); 229 } 230 231 auto cleanup = absl::MakeCleanup([&] { kill(pid, SIGKILL); }); 232 233 // Erase the string data in parent's copy of remote, to make sure we are 234 // reading data from the child. 235 remote.erase(); 236 237 // Compare against actual remote (not what we sent to the child, since we 238 // emptied it). 239 TestIovecs want_remote = TestIovecs(GetParam().remote_data); 240 auto want_size = std::min(local.bytes_, want_remote.bytes_); 241 ASSERT_THAT(process_vm_readv(pid, local.iovecs_.data(), local.iovecs_.size(), 242 remote.iovecs_.data(), remote.iovecs_.size(), 0), 243 SyscallSucceedsWithValue(want_size)); 244 EXPECT_TRUE(bytes_match(local, want_remote, want_size)); 245 } 246 247 // TestWritevSameProcess calls process_vm_readv in the same process with various 248 // local/remote buffers. 249 TEST_P(ProcessVMTest, TestWritevSameProcess) { 250 TestIovecs local(GetParam().local_data); 251 TestIovecs remote(GetParam().remote_data); 252 253 auto want_size = std::min(remote.bytes_, local.bytes_); 254 EXPECT_THAT( 255 process_vm_writev(getpid(), local.iovecs_.data(), local.iovecs_.size(), 256 remote.iovecs_.data(), remote.iovecs_.size(), 0), 257 SyscallSucceedsWithValue(want_size)); 258 EXPECT_TRUE(bytes_match(local, remote, want_size)); 259 } 260 261 // TestWritevSubProcess writes data to a forked child process. 262 TEST_P(ProcessVMTest, TestWritevSubProcess) { 263 TestIovecs local(GetParam().local_data); 264 TestIovecs remote(GetParam().remote_data); 265 266 // A pipe is used to wait on the write call from the parent, so we can block 267 // asserting until the write is complete. 268 int pipefd[2]; 269 ASSERT_THAT(pipe(pipefd), SyscallSucceeds()); 270 271 pid_t pid = fork(); 272 TEST_CHECK_SUCCESS(pid); 273 if (pid == 0) { 274 // Child. This is the "remote" process. 275 close(pipefd[1]); 276 277 // Wait on pipefd. It will be closed after the parent has written. 278 char buf; 279 TEST_CHECK_SUCCESS(read(pipefd[0], &buf, sizeof(buf))); 280 close(pipefd[0]); 281 282 // Check the data. This will exit non-0 in the case of a mismatch. 283 auto want_size = std::min(local.bytes_, remote.bytes_); 284 TEST_CHECK(bytes_match(local, remote, want_size)); 285 286 _exit(0); 287 } 288 289 auto cleanup = absl::MakeCleanup([&] { 290 int status = 0; 291 EXPECT_THAT(waitpid(pid, &status, 0), SyscallSucceeds()); 292 EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); 293 }); 294 295 // Write to the remote. 296 auto want_size = std::min(local.bytes_, remote.bytes_); 297 ASSERT_THAT( 298 process_vm_writev(pid, local.iovecs_.data(), local.iovecs_.size(), 299 remote.iovecs_.data(), remote.iovecs_.size(), 0), 300 SyscallSucceedsWithValue(want_size)); 301 302 // Now that we've written, close pipefd to signal the child can continue. 303 close(pipefd[1]); 304 } 305 306 TEST(ProcessVMInvalidTest, NonZeroFlags) { 307 struct iovec iov = {}; 308 // Flags should be 0. 309 EXPECT_THAT(process_vm_readv(0, &iov, 1, &iov, 1, 10), 310 SyscallFailsWithErrno(EINVAL)); 311 EXPECT_THAT(process_vm_writev(0, &iov, 1, &iov, 1, 10), 312 SyscallFailsWithErrno(EINVAL)); 313 } 314 315 TEST(ProcessVMInvalidTest, NullLocalIovec) { 316 struct iovec iov = {}; 317 pid_t child = fork(); 318 if (child == 0) { 319 sleep(10); // NOLINT - SleepFor is not async-signal-safe. 320 _exit(0); 321 } 322 323 EXPECT_THAT(process_vm_readv(child, nullptr, 1, &iov, 1, 0), 324 SyscallFailsWithErrno(EFAULT)); 325 326 EXPECT_THAT(process_vm_writev(child, nullptr, 1, &iov, 1, 0), 327 SyscallFailsWithErrno(EFAULT)); 328 EXPECT_THAT(kill(child, SIGKILL), SyscallSucceeds()); 329 EXPECT_THAT(waitpid(child, 0, 0), SyscallSucceeds()); 330 } 331 332 TEST(ProcessVMInvalidTest, NULLRemoteIovec) { 333 std::string contents = "3263827"; 334 struct iovec iov; 335 iov.iov_base = contents.data(); 336 iov.iov_len = contents.size(); 337 338 pid_t pid = fork(); 339 TEST_CHECK_SUCCESS(pid); 340 if (pid == 0) { 341 sleep(10); // NOLINT - SleepFor is not async-signal-safe. 342 _exit(0); 343 } 344 auto cleanup = absl::MakeCleanup([&] { kill(pid, SIGKILL); }); 345 346 EXPECT_THAT(process_vm_readv(pid, &iov, contents.length(), nullptr, 1, 0), 347 SyscallFailsWithErrno(::testing::AnyOf(EFAULT, EINVAL))); 348 EXPECT_THAT(process_vm_writev(pid, &iov, contents.length(), nullptr, 1, 0), 349 SyscallFailsWithErrno(::testing::AnyOf(EFAULT, EINVAL))); 350 } 351 352 TEST(ProcessVMInvalidTest, ProcessNoExist) { 353 struct iovec iov; 354 EXPECT_THAT(process_vm_readv(-1, &iov, 1, &iov, 1, 0), 355 SyscallFailsWithErrno(::testing::AnyOf(ESRCH, EFAULT))); 356 EXPECT_THAT(process_vm_writev(-1, &iov, 1, &iov, 1, 0), 357 SyscallFailsWithErrno(::testing::AnyOf(ESRCH, EFAULT))); 358 } 359 360 TEST(ProcessVMInvalidTest, GreaterThanIOV_MAX) { 361 std::string contents = "3263827"; 362 struct iovec iov; 363 iov.iov_base = contents.data(); 364 iov.iov_len = contents.size(); 365 366 pid_t pid = fork(); 367 TEST_CHECK_SUCCESS(pid); 368 if (pid == 0) { 369 sleep(10); // NOLINT - SleepFor is not async-signal-safe. 370 _exit(0); 371 } 372 auto cleanup = absl::MakeCleanup([&] { kill(pid, SIGKILL); }); 373 374 EXPECT_THAT(process_vm_readv(pid, &iov, 1, &iov, IOV_MAX + 1, 0), 375 SyscallFailsWithErrno(EINVAL)); 376 EXPECT_THAT(process_vm_writev(pid, &iov, 1, &iov, IOV_MAX + 1, 0), 377 SyscallFailsWithErrno(EINVAL)); 378 } 379 380 TEST(ProcessVMInvalidTest, PartialReadWrite) { 381 std::string iov_content_1 = "1138"; 382 std::string iov_content_2 = "3720"; 383 struct iovec iov[2]; 384 iov[0].iov_base = iov_content_1.data(); 385 iov[0].iov_len = iov_content_1.size(); 386 iov[1].iov_base = iov_content_2.data(); 387 iov[1].iov_len = iov_content_2.size(); 388 389 std::string iov_corrupted_content_1 = iov_content_1; 390 struct iovec corrupted_iov[2]; 391 corrupted_iov[0].iov_base = iov_corrupted_content_1.data(); 392 corrupted_iov[0].iov_len = iov_corrupted_content_1.size(); 393 corrupted_iov[1].iov_base = (void*)0xDEADBEEF; 394 corrupted_iov[1].iov_len = 42; 395 396 EXPECT_THAT( 397 RetryEINTR(process_vm_writev)(getpid(), iov, 2, corrupted_iov, 2, 0), 398 SyscallSucceedsWithValue(iov_content_1.size())); 399 EXPECT_THAT( 400 RetryEINTR(process_vm_readv)(getpid(), corrupted_iov, 2, iov, 2, 0), 401 SyscallSucceedsWithValue(iov_content_1.size())); 402 EXPECT_THAT( 403 RetryEINTR(process_vm_writev)(getpid(), corrupted_iov, 2, iov, 2, 0), 404 SyscallSucceedsWithValue(iov_content_1.size())); 405 EXPECT_THAT( 406 RetryEINTR(process_vm_readv)(getpid(), iov, 2, corrupted_iov, 2, 0), 407 SyscallSucceedsWithValue(iov_content_1.size())); 408 } 409 410 TEST(ProcessVMInvalidTest, AccessInvalidMemoryFailsWithEINVAL) { 411 auto const mapping = 412 ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_PRIVATE)); 413 struct iovec iov_none, iov_valid; 414 char buf[128]; 415 iov_valid.iov_base = buf; 416 iov_valid.iov_len = sizeof(buf); 417 iov_none.iov_base = mapping.ptr(); 418 iov_none.iov_len = mapping.len(); 419 420 EXPECT_THAT( 421 RetryEINTR(process_vm_writev)(getpid(), &iov_none, 1, &iov_valid, 1, 0), 422 SyscallFailsWithErrno(EFAULT)); 423 EXPECT_THAT( 424 RetryEINTR(process_vm_writev)(getpid(), &iov_valid, 1, &iov_none, 1, 0), 425 SyscallFailsWithErrno(EFAULT)); 426 } 427 428 TEST(ProcessVMTest, WriteToZombie) { 429 char* data = {0}; 430 pid_t child; 431 ASSERT_THAT(child = fork(), SyscallSucceeds()); 432 if (child == 0) { 433 _exit(0); 434 } 435 siginfo_t siginfo = {}; 436 ASSERT_THAT(RetryEINTR(waitid)(P_PID, child, &siginfo, WEXITED | WNOWAIT), 437 SyscallSucceeds()); 438 struct iovec iov; 439 iov.iov_base = data; 440 iov.iov_len = sizeof(data); 441 ASSERT_THAT(process_vm_writev(child, &iov, 1, &iov, 1, 0), 442 SyscallFailsWithErrno(ESRCH)); 443 } 444 445 // TestReadvNull calls process_vm_readv with null iovecs and checks that they 446 // succeed but return 0; 447 TEST(ProcessVMTest, TestReadvNull) { 448 TestIovecs local(std::vector<std::string>{"foo"}); 449 TestIovecs remote(std::vector<std::string>{"bar"}); 450 451 // Pass 0 for local. 452 EXPECT_THAT(process_vm_readv(getpid(), 0, 0, remote.iovecs_.data(), 453 remote.iovecs_.size(), 0), 454 SyscallSucceedsWithValue(0)); 455 456 // Pass 0 for remote. 457 EXPECT_THAT(process_vm_readv(getpid(), local.iovecs_.data(), 458 local.iovecs_.size(), 0, 0, 0), 459 SyscallSucceedsWithValue(0)); 460 } 461 462 // TestWritevNull calls process_vm_writev with null iovecs and checks that they 463 // succeed but return 0; 464 TEST(ProcessVMTest, TestWritevNull) { 465 TestIovecs local(std::vector<std::string>{"foo"}); 466 TestIovecs remote(std::vector<std::string>{"bar"}); 467 468 // Pass 0 for local. 469 EXPECT_THAT(process_vm_writev(getpid(), 0, 0, remote.iovecs_.data(), 470 remote.iovecs_.size(), 0), 471 SyscallSucceedsWithValue(0)); 472 473 // Pass 0 for remote. 474 EXPECT_THAT(process_vm_writev(getpid(), local.iovecs_.data(), 475 local.iovecs_.size(), 0, 0, 0), 476 SyscallSucceedsWithValue(0)); 477 } 478 479 } // namespace 480 } // namespace testing 481 } // namespace gvisor