github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/syscalls/linux/exec.cc (about) 1 // Copyright 2018 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 "test/syscalls/linux/exec.h" 16 17 #include <errno.h> 18 #include <fcntl.h> 19 #include <sys/eventfd.h> 20 #include <sys/resource.h> 21 #include <sys/time.h> 22 #include <unistd.h> 23 24 #include <iostream> 25 #include <memory> 26 #include <string> 27 #include <vector> 28 29 #include "gtest/gtest.h" 30 #include "absl/strings/match.h" 31 #include "absl/strings/numbers.h" 32 #include "absl/strings/str_cat.h" 33 #include "absl/strings/str_split.h" 34 #include "absl/strings/string_view.h" 35 #include "absl/synchronization/mutex.h" 36 #include "absl/types/optional.h" 37 #include "test/util/file_descriptor.h" 38 #include "test/util/fs_util.h" 39 #include "test/util/multiprocess_util.h" 40 #include "test/util/posix_error.h" 41 #include "test/util/temp_path.h" 42 #include "test/util/test_util.h" 43 #include "test/util/thread_util.h" 44 45 namespace gvisor { 46 namespace testing { 47 48 namespace { 49 50 constexpr char kBasicWorkload[] = "test/syscalls/linux/exec_basic_workload"; 51 constexpr char kExitScript[] = "test/syscalls/linux/exit_script"; 52 constexpr char kStateWorkload[] = "test/syscalls/linux/exec_state_workload"; 53 constexpr char kProcExeWorkload[] = 54 "test/syscalls/linux/exec_proc_exe_workload"; 55 constexpr char kAssertClosedWorkload[] = 56 "test/syscalls/linux/exec_assert_closed_workload"; 57 constexpr char kPriorityWorkload[] = "test/syscalls/linux/priority_execve"; 58 59 constexpr char kExit42[] = "--exec_exit_42"; 60 constexpr char kExecWithThread[] = "--exec_exec_with_thread"; 61 constexpr char kExecFromThread[] = "--exec_exec_from_thread"; 62 63 // Runs file specified by dirfd and pathname with argv and checks that the exit 64 // status is expect_status and that stderr contains expect_stderr. 65 void CheckExecHelper(const absl::optional<int32_t> dirfd, 66 const std::string& pathname, const ExecveArray& argv, 67 const ExecveArray& envv, const int flags, 68 int expect_status, const std::string& expect_stderr) { 69 int pipe_fds[2]; 70 ASSERT_THAT(pipe2(pipe_fds, O_CLOEXEC), SyscallSucceeds()); 71 72 FileDescriptor read_fd(pipe_fds[0]); 73 FileDescriptor write_fd(pipe_fds[1]); 74 75 pid_t child; 76 int execve_errno; 77 78 const auto remap_stderr = [pipe_fds] { 79 // Remap stdin and stdout to /dev/null. 80 int fd = open("/dev/null", O_RDWR | O_CLOEXEC); 81 if (fd < 0) { 82 _exit(errno); 83 } 84 85 int ret = dup2(fd, 0); 86 if (ret < 0) { 87 _exit(errno); 88 } 89 90 ret = dup2(fd, 1); 91 if (ret < 0) { 92 _exit(errno); 93 } 94 95 // And stderr to the pipe. 96 ret = dup2(pipe_fds[1], 2); 97 if (ret < 0) { 98 _exit(errno); 99 } 100 101 // Here, we'd ideally close all other FDs inherited from the parent. 102 // However, that's not worth the effort and CloexecNormalFile and 103 // CloexecEventfd depend on that not happening. 104 }; 105 106 Cleanup kill; 107 if (dirfd.has_value()) { 108 kill = ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(*dirfd, pathname, argv, 109 envv, flags, remap_stderr, 110 &child, &execve_errno)); 111 } else { 112 kill = ASSERT_NO_ERRNO_AND_VALUE( 113 ForkAndExec(pathname, argv, envv, remap_stderr, &child, &execve_errno)); 114 } 115 116 ASSERT_EQ(0, execve_errno); 117 118 // Not needed anymore. 119 write_fd.reset(); 120 121 // Read stderr until the child exits. 122 std::string output; 123 constexpr int kSize = 128; 124 char buf[kSize]; 125 int n; 126 do { 127 ASSERT_THAT(n = ReadFd(read_fd.get(), buf, kSize), SyscallSucceeds()); 128 if (n > 0) { 129 output.append(buf, n); 130 } 131 } while (n > 0); 132 133 int status; 134 ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), SyscallSucceeds()); 135 EXPECT_EQ(status, expect_status); 136 137 // Process cleanup no longer needed. 138 kill.Release(); 139 140 EXPECT_TRUE(absl::StrContains(output, expect_stderr)) << output; 141 } 142 143 void CheckExec(const std::string& filename, const ExecveArray& argv, 144 const ExecveArray& envv, int expect_status, 145 const std::string& expect_stderr) { 146 CheckExecHelper(/*dirfd=*/absl::optional<int32_t>(), filename, argv, envv, 147 /*flags=*/0, expect_status, expect_stderr); 148 } 149 150 void CheckExecveat(const int32_t dirfd, const std::string& pathname, 151 const ExecveArray& argv, const ExecveArray& envv, 152 const int flags, int expect_status, 153 const std::string& expect_stderr) { 154 CheckExecHelper(absl::optional<int32_t>(dirfd), pathname, argv, envv, flags, 155 expect_status, expect_stderr); 156 } 157 158 TEST(ExecTest, EmptyPath) { 159 int execve_errno; 160 ASSERT_NO_ERRNO_AND_VALUE(ForkAndExec("", {}, {}, nullptr, &execve_errno)); 161 EXPECT_EQ(execve_errno, ENOENT); 162 } 163 164 TEST(ExecTest, Basic) { 165 CheckExec(RunfilePath(kBasicWorkload), {RunfilePath(kBasicWorkload)}, {}, 166 ArgEnvExitStatus(0, 0), 167 absl::StrCat(RunfilePath(kBasicWorkload), "\n")); 168 } 169 170 TEST(ExecTest, OneArg) { 171 CheckExec(RunfilePath(kBasicWorkload), {RunfilePath(kBasicWorkload), "1"}, {}, 172 ArgEnvExitStatus(1, 0), 173 absl::StrCat(RunfilePath(kBasicWorkload), "\n1\n")); 174 } 175 176 TEST(ExecTest, FiveArg) { 177 CheckExec(RunfilePath(kBasicWorkload), 178 {RunfilePath(kBasicWorkload), "1", "2", "3", "4", "5"}, {}, 179 ArgEnvExitStatus(5, 0), 180 absl::StrCat(RunfilePath(kBasicWorkload), "\n1\n2\n3\n4\n5\n")); 181 } 182 183 TEST(ExecTest, OneEnv) { 184 CheckExec(RunfilePath(kBasicWorkload), {RunfilePath(kBasicWorkload)}, {"1"}, 185 ArgEnvExitStatus(0, 1), 186 absl::StrCat(RunfilePath(kBasicWorkload), "\n1\n")); 187 } 188 189 TEST(ExecTest, FiveEnv) { 190 CheckExec(RunfilePath(kBasicWorkload), {RunfilePath(kBasicWorkload)}, 191 {"1", "2", "3", "4", "5"}, ArgEnvExitStatus(0, 5), 192 absl::StrCat(RunfilePath(kBasicWorkload), "\n1\n2\n3\n4\n5\n")); 193 } 194 195 TEST(ExecTest, OneArgOneEnv) { 196 CheckExec(RunfilePath(kBasicWorkload), {RunfilePath(kBasicWorkload), "arg"}, 197 {"env"}, ArgEnvExitStatus(1, 1), 198 absl::StrCat(RunfilePath(kBasicWorkload), "\narg\nenv\n")); 199 } 200 201 TEST(ExecTest, InterpreterScript) { 202 CheckExec(RunfilePath(kExitScript), {RunfilePath(kExitScript), "25"}, {}, 203 ArgEnvExitStatus(25, 0), ""); 204 } 205 206 // Everything after the path in the interpreter script is a single argument. 207 TEST(ExecTest, InterpreterScriptArgSplit) { 208 // Symlink through /tmp to ensure the path is short enough. 209 TempPath link = ASSERT_NO_ERRNO_AND_VALUE( 210 TempPath::CreateSymlinkTo("/tmp", RunfilePath(kBasicWorkload))); 211 212 TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 213 GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path(), " foo bar"), 214 0755)); 215 216 CheckExec(script.path(), {script.path()}, {}, ArgEnvExitStatus(2, 0), 217 absl::StrCat(link.path(), "\nfoo bar\n", script.path(), "\n")); 218 } 219 220 // Original argv[0] is replaced with the script path. 221 TEST(ExecTest, InterpreterScriptArgvZero) { 222 // Symlink through /tmp to ensure the path is short enough. 223 TempPath link = ASSERT_NO_ERRNO_AND_VALUE( 224 TempPath::CreateSymlinkTo("/tmp", RunfilePath(kBasicWorkload))); 225 226 TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 227 GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path()), 0755)); 228 229 CheckExec(script.path(), {"REPLACED"}, {}, ArgEnvExitStatus(1, 0), 230 absl::StrCat(link.path(), "\n", script.path(), "\n")); 231 } 232 233 // Original argv[0] is replaced with the script path, exactly as passed to 234 // execve. 235 TEST(ExecTest, InterpreterScriptArgvZeroRelative) { 236 // Symlink through /tmp to ensure the path is short enough. 237 TempPath link = ASSERT_NO_ERRNO_AND_VALUE( 238 TempPath::CreateSymlinkTo("/tmp", RunfilePath(kBasicWorkload))); 239 240 TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 241 GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path()), 0755)); 242 243 auto cwd = ASSERT_NO_ERRNO_AND_VALUE(GetCWD()); 244 auto script_relative = 245 ASSERT_NO_ERRNO_AND_VALUE(GetRelativePath(cwd, script.path())); 246 247 CheckExec(script_relative, {"REPLACED"}, {}, ArgEnvExitStatus(1, 0), 248 absl::StrCat(link.path(), "\n", script_relative, "\n")); 249 } 250 251 // argv[0] is added as the script path, even if there was none. 252 TEST(ExecTest, InterpreterScriptArgvZeroAdded) { 253 // Symlink through /tmp to ensure the path is short enough. 254 TempPath link = ASSERT_NO_ERRNO_AND_VALUE( 255 TempPath::CreateSymlinkTo("/tmp", RunfilePath(kBasicWorkload))); 256 257 TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 258 GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path()), 0755)); 259 260 CheckExec(script.path(), {}, {}, ArgEnvExitStatus(1, 0), 261 absl::StrCat(link.path(), "\n", script.path(), "\n")); 262 } 263 264 // A NUL byte in the script line ends parsing. 265 TEST(ExecTest, InterpreterScriptArgNUL) { 266 // Symlink through /tmp to ensure the path is short enough. 267 TempPath link = ASSERT_NO_ERRNO_AND_VALUE( 268 TempPath::CreateSymlinkTo("/tmp", RunfilePath(kBasicWorkload))); 269 270 TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 271 GetAbsoluteTestTmpdir(), 272 absl::StrCat("#!", link.path(), " foo", std::string(1, '\0'), "bar"), 273 0755)); 274 275 CheckExec(script.path(), {script.path()}, {}, ArgEnvExitStatus(2, 0), 276 absl::StrCat(link.path(), "\nfoo\n", script.path(), "\n")); 277 } 278 279 // Trailing whitespace following interpreter path is ignored. 280 TEST(ExecTest, InterpreterScriptTrailingWhitespace) { 281 // Symlink through /tmp to ensure the path is short enough. 282 TempPath link = ASSERT_NO_ERRNO_AND_VALUE( 283 TempPath::CreateSymlinkTo("/tmp", RunfilePath(kBasicWorkload))); 284 285 TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 286 GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path(), " \n"), 0755)); 287 288 CheckExec(script.path(), {script.path()}, {}, ArgEnvExitStatus(1, 0), 289 absl::StrCat(link.path(), "\n", script.path(), "\n")); 290 } 291 292 // Multiple whitespace characters between interpreter and arg allowed. 293 TEST(ExecTest, InterpreterScriptArgWhitespace) { 294 // Symlink through /tmp to ensure the path is short enough. 295 TempPath link = ASSERT_NO_ERRNO_AND_VALUE( 296 TempPath::CreateSymlinkTo("/tmp", RunfilePath(kBasicWorkload))); 297 298 TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 299 GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path(), " foo"), 0755)); 300 301 CheckExec(script.path(), {script.path()}, {}, ArgEnvExitStatus(2, 0), 302 absl::StrCat(link.path(), "\nfoo\n", script.path(), "\n")); 303 } 304 305 TEST(ExecTest, InterpreterScriptNoPath) { 306 TempPath script = ASSERT_NO_ERRNO_AND_VALUE( 307 TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), "#!\n\n", 0755)); 308 309 int execve_errno; 310 ASSERT_NO_ERRNO_AND_VALUE( 311 ForkAndExec(script.path(), {script.path()}, {}, nullptr, &execve_errno)); 312 EXPECT_EQ(execve_errno, ENOEXEC); 313 } 314 315 // AT_EXECFN is the path passed to execve. 316 TEST(ExecTest, ExecFn) { 317 // Symlink through /tmp to ensure the path is short enough. 318 TempPath link = ASSERT_NO_ERRNO_AND_VALUE( 319 TempPath::CreateSymlinkTo("/tmp", RunfilePath(kStateWorkload))); 320 321 TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 322 GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path(), " PrintExecFn"), 323 0755)); 324 325 // Pass the script as a relative path and assert that is what appears in 326 // AT_EXECFN. 327 auto cwd = ASSERT_NO_ERRNO_AND_VALUE(GetCWD()); 328 auto script_relative = 329 ASSERT_NO_ERRNO_AND_VALUE(GetRelativePath(cwd, script.path())); 330 331 CheckExec(script_relative, {script_relative}, {}, ArgEnvExitStatus(0, 0), 332 absl::StrCat(script_relative, "\n")); 333 } 334 335 TEST(ExecTest, ExecName) { 336 std::string path = RunfilePath(kStateWorkload); 337 338 CheckExec(path, {path, "PrintExecName"}, {}, ArgEnvExitStatus(0, 0), 339 absl::StrCat(Basename(path).substr(0, 15), "\n")); 340 } 341 342 TEST(ExecTest, ExecNameScript) { 343 // Symlink through /tmp to ensure the path is short enough. 344 TempPath link = ASSERT_NO_ERRNO_AND_VALUE( 345 TempPath::CreateSymlinkTo("/tmp", RunfilePath(kStateWorkload))); 346 347 TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 348 GetAbsoluteTestTmpdir(), 349 absl::StrCat("#!", link.path(), " PrintExecName"), 0755)); 350 351 std::string script_path = script.path(); 352 353 CheckExec(script_path, {script_path}, {}, ArgEnvExitStatus(0, 0), 354 absl::StrCat(Basename(script_path).substr(0, 15), "\n")); 355 } 356 357 // execve may be called by a multithreaded process. 358 TEST(ExecTest, WithSiblingThread) { 359 CheckExec("/proc/self/exe", {"/proc/self/exe", kExecWithThread}, {}, 360 W_EXITCODE(42, 0), ""); 361 } 362 363 // execve may be called from a thread other than the leader of a multithreaded 364 // process. 365 TEST(ExecTest, FromSiblingThread) { 366 CheckExec("/proc/self/exe", {"/proc/self/exe", kExecFromThread}, {}, 367 W_EXITCODE(42, 0), ""); 368 } 369 370 TEST(ExecTest, NotFound) { 371 char* const argv[] = {nullptr}; 372 char* const envp[] = {nullptr}; 373 EXPECT_THAT(execve("/file/does/not/exist", argv, envp), 374 SyscallFailsWithErrno(ENOENT)); 375 } 376 377 TEST(ExecTest, NoExecPerm) { 378 char* const argv[] = {nullptr}; 379 char* const envp[] = {nullptr}; 380 auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 381 EXPECT_THAT(execve(f.path().c_str(), argv, envp), 382 SyscallFailsWithErrno(EACCES)); 383 } 384 385 // A signal handler we never expect to be called. 386 void SignalHandler(int signo) { 387 std::cerr << "Signal " << signo << " raised." << std::endl; 388 exit(1); 389 } 390 391 // Signal handlers are reset on execve(2), unless they have default or ignored 392 // disposition. 393 TEST(ExecStateTest, HandlerReset) { 394 struct sigaction sa; 395 sa.sa_handler = SignalHandler; 396 ASSERT_THAT(sigaction(SIGUSR1, &sa, nullptr), SyscallSucceeds()); 397 398 ExecveArray args = { 399 RunfilePath(kStateWorkload), 400 "CheckSigHandler", 401 absl::StrCat(SIGUSR1), 402 absl::StrCat(absl::Hex(reinterpret_cast<uintptr_t>(SIG_DFL))), 403 }; 404 405 CheckExec(RunfilePath(kStateWorkload), args, {}, W_EXITCODE(0, 0), ""); 406 } 407 408 // Ignored signal dispositions are not reset. 409 TEST(ExecStateTest, IgnorePreserved) { 410 struct sigaction sa; 411 sa.sa_handler = SIG_IGN; 412 ASSERT_THAT(sigaction(SIGUSR1, &sa, nullptr), SyscallSucceeds()); 413 414 ExecveArray args = { 415 RunfilePath(kStateWorkload), 416 "CheckSigHandler", 417 absl::StrCat(SIGUSR1), 418 absl::StrCat(absl::Hex(reinterpret_cast<uintptr_t>(SIG_IGN))), 419 }; 420 421 CheckExec(RunfilePath(kStateWorkload), args, {}, W_EXITCODE(0, 0), ""); 422 } 423 424 // Signal masks are not reset on exec 425 TEST(ExecStateTest, SignalMask) { 426 sigset_t s; 427 sigemptyset(&s); 428 sigaddset(&s, SIGUSR1); 429 ASSERT_THAT(sigprocmask(SIG_BLOCK, &s, nullptr), SyscallSucceeds()); 430 431 ExecveArray args = { 432 RunfilePath(kStateWorkload), 433 "CheckSigBlocked", 434 absl::StrCat(SIGUSR1), 435 }; 436 437 CheckExec(RunfilePath(kStateWorkload), args, {}, W_EXITCODE(0, 0), ""); 438 } 439 440 // itimers persist across execve. 441 // N.B. Timers created with timer_create(2) should not be preserved! 442 TEST(ExecStateTest, ItimerPreserved) { 443 // The fork in ForkAndExec clears itimers, so only set them up after fork. 444 auto setup_itimer = [] { 445 // Ignore SIGALRM, as we don't actually care about timer 446 // expirations. 447 struct sigaction sa; 448 sa.sa_handler = SIG_IGN; 449 int ret = sigaction(SIGALRM, &sa, nullptr); 450 if (ret < 0) { 451 _exit(errno); 452 } 453 454 struct itimerval itv; 455 itv.it_interval.tv_sec = 1; 456 itv.it_interval.tv_usec = 0; 457 itv.it_value.tv_sec = 1; 458 itv.it_value.tv_usec = 0; 459 ret = setitimer(ITIMER_REAL, &itv, nullptr); 460 if (ret < 0) { 461 _exit(errno); 462 } 463 }; 464 465 std::string filename = RunfilePath(kStateWorkload); 466 ExecveArray argv = { 467 filename, 468 "CheckItimerEnabled", 469 absl::StrCat(ITIMER_REAL), 470 }; 471 472 pid_t child; 473 int execve_errno; 474 auto kill = ASSERT_NO_ERRNO_AND_VALUE( 475 ForkAndExec(filename, argv, {}, setup_itimer, &child, &execve_errno)); 476 ASSERT_EQ(0, execve_errno); 477 478 int status; 479 ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), SyscallSucceeds()); 480 EXPECT_EQ(0, status); 481 482 // Process cleanup no longer needed. 483 kill.Release(); 484 } 485 486 TEST(ProcSelfExe, ChangesAcrossExecve) { 487 // See exec_proc_exe_workload for more details. We simply 488 // assert that the /proc/self/exe link changes across execve. 489 CheckExec(RunfilePath(kProcExeWorkload), 490 {RunfilePath(kProcExeWorkload), 491 ASSERT_NO_ERRNO_AND_VALUE(ProcessExePath(getpid()))}, 492 {}, W_EXITCODE(0, 0), ""); 493 } 494 495 TEST(ExecTest, CloexecNormalFile) { 496 TempPath tempFile = ASSERT_NO_ERRNO_AND_VALUE( 497 TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), "bar", 0755)); 498 const FileDescriptor fd_closed_on_exec = 499 ASSERT_NO_ERRNO_AND_VALUE(Open(tempFile.path(), O_RDONLY | O_CLOEXEC)); 500 501 CheckExec(RunfilePath(kAssertClosedWorkload), 502 {RunfilePath(kAssertClosedWorkload), 503 absl::StrCat(fd_closed_on_exec.get())}, 504 {}, W_EXITCODE(0, 0), ""); 505 506 // The assert closed workload exits with code 2 if the file still exists. We 507 // can use this to do a negative test. 508 const FileDescriptor fd_open_on_exec = 509 ASSERT_NO_ERRNO_AND_VALUE(Open(tempFile.path(), O_RDONLY)); 510 511 CheckExec( 512 RunfilePath(kAssertClosedWorkload), 513 {RunfilePath(kAssertClosedWorkload), absl::StrCat(fd_open_on_exec.get())}, 514 {}, W_EXITCODE(2, 0), ""); 515 } 516 517 TEST(ExecTest, CloexecEventfd) { 518 int efd; 519 ASSERT_THAT(efd = eventfd(0, EFD_CLOEXEC), SyscallSucceeds()); 520 FileDescriptor fd(efd); 521 522 CheckExec(RunfilePath(kAssertClosedWorkload), 523 {RunfilePath(kAssertClosedWorkload), absl::StrCat(fd.get())}, {}, 524 W_EXITCODE(0, 0), ""); 525 } 526 527 constexpr int kLinuxMaxSymlinks = 40; 528 529 TEST(ExecTest, SymlinkLimitExceeded) { 530 std::string path = RunfilePath(kBasicWorkload); 531 532 // Hold onto TempPath objects so they are not destructed prematurely. 533 std::vector<TempPath> symlinks; 534 for (int i = 0; i < kLinuxMaxSymlinks + 1; i++) { 535 symlinks.push_back( 536 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateSymlinkTo("/tmp", path))); 537 path = symlinks[i].path(); 538 } 539 540 int execve_errno; 541 ASSERT_NO_ERRNO_AND_VALUE( 542 ForkAndExec(path, {path}, {}, /*child=*/nullptr, &execve_errno)); 543 EXPECT_EQ(execve_errno, ELOOP); 544 } 545 546 TEST(ExecTest, SymlinkLimitRefreshedForInterpreter) { 547 std::string tmp_dir = "/tmp"; 548 std::string interpreter_path = "/bin/echo"; 549 TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 550 tmp_dir, absl::StrCat("#!", interpreter_path), 0755)); 551 std::string script_path = script.path(); 552 553 // Hold onto TempPath objects so they are not destructed prematurely. 554 std::vector<TempPath> interpreter_symlinks; 555 std::vector<TempPath> script_symlinks; 556 // Replace both the interpreter and script paths with symlink chains of just 557 // over half the symlink limit each; this is the minimum required to test that 558 // the symlink limit applies separately to each traversal, while tolerating 559 // some symlinks in the resolution of (the original) interpreter_path and 560 // script_path. 561 for (int i = 0; i < (kLinuxMaxSymlinks / 2) + 1; i++) { 562 interpreter_symlinks.push_back(ASSERT_NO_ERRNO_AND_VALUE( 563 TempPath::CreateSymlinkTo(tmp_dir, interpreter_path))); 564 interpreter_path = interpreter_symlinks[i].path(); 565 script_symlinks.push_back(ASSERT_NO_ERRNO_AND_VALUE( 566 TempPath::CreateSymlinkTo(tmp_dir, script_path))); 567 script_path = script_symlinks[i].path(); 568 } 569 570 CheckExec(script_path, {script_path}, {}, ArgEnvExitStatus(0, 0), ""); 571 } 572 573 TEST(ExecveatTest, BasicWithFDCWD) { 574 std::string path = RunfilePath(kBasicWorkload); 575 CheckExecveat(AT_FDCWD, path, {path}, {}, /*flags=*/0, ArgEnvExitStatus(0, 0), 576 absl::StrCat(path, "\n")); 577 } 578 579 TEST(ExecveatTest, Basic) { 580 std::string absolute_path = RunfilePath(kBasicWorkload); 581 std::string parent_dir = std::string(Dirname(absolute_path)); 582 std::string base = std::string(Basename(absolute_path)); 583 const FileDescriptor dirfd = 584 ASSERT_NO_ERRNO_AND_VALUE(Open(parent_dir, O_DIRECTORY)); 585 586 CheckExecveat(dirfd.get(), base, {absolute_path}, {}, /*flags=*/0, 587 ArgEnvExitStatus(0, 0), absl::StrCat(absolute_path, "\n")); 588 } 589 590 TEST(ExecveatTest, FDNotADirectory) { 591 std::string absolute_path = RunfilePath(kBasicWorkload); 592 std::string base = std::string(Basename(absolute_path)); 593 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(absolute_path, 0)); 594 595 int execve_errno; 596 ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(fd.get(), base, {absolute_path}, {}, 597 /*flags=*/0, /*child=*/nullptr, 598 &execve_errno)); 599 EXPECT_EQ(execve_errno, ENOTDIR); 600 } 601 602 TEST(ExecveatTest, AbsolutePathWithFDCWD) { 603 std::string path = RunfilePath(kBasicWorkload); 604 CheckExecveat(AT_FDCWD, path, {path}, {}, ArgEnvExitStatus(0, 0), 0, 605 absl::StrCat(path, "\n")); 606 } 607 608 TEST(ExecveatTest, AbsolutePath) { 609 std::string path = RunfilePath(kBasicWorkload); 610 // File descriptor should be ignored when an absolute path is given. 611 const int32_t badFD = -1; 612 CheckExecveat(badFD, path, {path}, {}, ArgEnvExitStatus(0, 0), 0, 613 absl::StrCat(path, "\n")); 614 } 615 616 TEST(ExecveatTest, EmptyPathBasic) { 617 std::string path = RunfilePath(kBasicWorkload); 618 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_PATH)); 619 620 CheckExecveat(fd.get(), "", {path}, {}, AT_EMPTY_PATH, ArgEnvExitStatus(0, 0), 621 absl::StrCat(path, "\n")); 622 } 623 624 TEST(ExecveatTest, EmptyPathWithDirFD) { 625 std::string path = RunfilePath(kBasicWorkload); 626 std::string parent_dir = std::string(Dirname(path)); 627 const FileDescriptor dirfd = 628 ASSERT_NO_ERRNO_AND_VALUE(Open(parent_dir, O_DIRECTORY)); 629 630 int execve_errno; 631 ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(dirfd.get(), "", {path}, {}, 632 AT_EMPTY_PATH, 633 /*child=*/nullptr, &execve_errno)); 634 EXPECT_EQ(execve_errno, EACCES); 635 } 636 637 TEST(ExecveatTest, EmptyPathWithoutEmptyPathFlag) { 638 std::string path = RunfilePath(kBasicWorkload); 639 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_PATH)); 640 641 int execve_errno; 642 ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat( 643 fd.get(), "", {path}, {}, /*flags=*/0, /*child=*/nullptr, &execve_errno)); 644 EXPECT_EQ(execve_errno, ENOENT); 645 } 646 647 TEST(ExecveatTest, AbsolutePathWithEmptyPathFlag) { 648 std::string path = RunfilePath(kBasicWorkload); 649 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_PATH)); 650 651 CheckExecveat(fd.get(), path, {path}, {}, AT_EMPTY_PATH, 652 ArgEnvExitStatus(0, 0), absl::StrCat(path, "\n")); 653 } 654 655 TEST(ExecveatTest, RelativePathWithEmptyPathFlag) { 656 std::string absolute_path = RunfilePath(kBasicWorkload); 657 std::string parent_dir = std::string(Dirname(absolute_path)); 658 std::string base = std::string(Basename(absolute_path)); 659 const FileDescriptor dirfd = 660 ASSERT_NO_ERRNO_AND_VALUE(Open(parent_dir, O_DIRECTORY)); 661 662 CheckExecveat(dirfd.get(), base, {absolute_path}, {}, AT_EMPTY_PATH, 663 ArgEnvExitStatus(0, 0), absl::StrCat(absolute_path, "\n")); 664 } 665 666 TEST(ExecveatTest, SymlinkNoFollowWithRelativePath) { 667 std::string parent_dir = "/tmp"; 668 TempPath link = ASSERT_NO_ERRNO_AND_VALUE( 669 TempPath::CreateSymlinkTo(parent_dir, RunfilePath(kBasicWorkload))); 670 const FileDescriptor dirfd = 671 ASSERT_NO_ERRNO_AND_VALUE(Open(parent_dir, O_DIRECTORY)); 672 std::string base = std::string(Basename(link.path())); 673 674 int execve_errno; 675 ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(dirfd.get(), base, {base}, {}, 676 AT_SYMLINK_NOFOLLOW, 677 /*child=*/nullptr, &execve_errno)); 678 EXPECT_EQ(execve_errno, ELOOP); 679 } 680 681 TEST(ExecveatTest, UnshareFiles) { 682 TempPath tempFile = ASSERT_NO_ERRNO_AND_VALUE( 683 TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), "bar", 0755)); 684 const FileDescriptor fd_closed_on_exec = 685 ASSERT_NO_ERRNO_AND_VALUE(Open(tempFile.path(), O_RDONLY | O_CLOEXEC)); 686 687 ExecveArray argv = {"test"}; 688 ExecveArray envp; 689 std::string child_path = RunfilePath(kBasicWorkload); 690 pid_t child = 691 syscall(__NR_clone, SIGCHLD | CLONE_VFORK | CLONE_FILES, 0, 0, 0, 0); 692 if (child == 0) { 693 execve(child_path.c_str(), argv.get(), envp.get()); 694 _exit(1); 695 } 696 ASSERT_THAT(child, SyscallSucceeds()); 697 698 int status; 699 ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), SyscallSucceeds()); 700 EXPECT_EQ(status, 0); 701 702 struct stat st; 703 EXPECT_THAT(fstat(fd_closed_on_exec.get(), &st), SyscallSucceeds()); 704 } 705 706 TEST(ExecveatTest, SymlinkNoFollowWithAbsolutePath) { 707 std::string parent_dir = "/tmp"; 708 TempPath link = ASSERT_NO_ERRNO_AND_VALUE( 709 TempPath::CreateSymlinkTo(parent_dir, RunfilePath(kBasicWorkload))); 710 std::string path = link.path(); 711 712 int execve_errno; 713 ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(AT_FDCWD, path, {path}, {}, 714 AT_SYMLINK_NOFOLLOW, 715 /*child=*/nullptr, &execve_errno)); 716 EXPECT_EQ(execve_errno, ELOOP); 717 } 718 719 TEST(ExecveatTest, SymlinkNoFollowAndEmptyPath) { 720 TempPath link = ASSERT_NO_ERRNO_AND_VALUE( 721 TempPath::CreateSymlinkTo("/tmp", RunfilePath(kBasicWorkload))); 722 std::string path = link.path(); 723 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, 0)); 724 725 CheckExecveat(fd.get(), "", {path}, {}, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, 726 ArgEnvExitStatus(0, 0), absl::StrCat(path, "\n")); 727 } 728 729 TEST(ExecveatTest, SymlinkNoFollowIgnoreSymlinkAncestor) { 730 TempPath parent_link = 731 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateSymlinkTo("/tmp", "/bin")); 732 std::string path_with_symlink = JoinPath(parent_link.path(), "echo"); 733 734 CheckExecveat(AT_FDCWD, path_with_symlink, {path_with_symlink}, {}, 735 AT_SYMLINK_NOFOLLOW, ArgEnvExitStatus(0, 0), ""); 736 } 737 738 TEST(ExecveatTest, SymlinkNoFollowWithNormalFile) { 739 const FileDescriptor dirfd = 740 ASSERT_NO_ERRNO_AND_VALUE(Open("/bin", O_DIRECTORY)); 741 742 CheckExecveat(dirfd.get(), "echo", {"echo"}, {}, AT_SYMLINK_NOFOLLOW, 743 ArgEnvExitStatus(0, 0), ""); 744 } 745 746 TEST(ExecveatTest, BasicWithCloexecFD) { 747 std::string path = RunfilePath(kBasicWorkload); 748 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_CLOEXEC)); 749 750 CheckExecveat(fd.get(), "", {path}, {}, AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH, 751 ArgEnvExitStatus(0, 0), absl::StrCat(path, "\n")); 752 } 753 754 TEST(ExecveatTest, InterpreterScriptWithCloexecFD) { 755 std::string path = RunfilePath(kExitScript); 756 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_CLOEXEC)); 757 758 int execve_errno; 759 ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(fd.get(), "", {path}, {}, 760 AT_EMPTY_PATH, /*child=*/nullptr, 761 &execve_errno)); 762 EXPECT_EQ(execve_errno, ENOENT); 763 } 764 765 TEST(ExecveatTest, InterpreterScriptWithCloexecDirFD) { 766 std::string absolute_path = RunfilePath(kExitScript); 767 std::string parent_dir = std::string(Dirname(absolute_path)); 768 std::string base = std::string(Basename(absolute_path)); 769 const FileDescriptor dirfd = 770 ASSERT_NO_ERRNO_AND_VALUE(Open(parent_dir, O_CLOEXEC | O_DIRECTORY)); 771 772 int execve_errno; 773 ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(dirfd.get(), base, {base}, {}, 774 /*flags=*/0, /*child=*/nullptr, 775 &execve_errno)); 776 EXPECT_EQ(execve_errno, ENOENT); 777 } 778 779 TEST(ExecveatTest, InvalidFlags) { 780 int execve_errno; 781 ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat( 782 /*dirfd=*/-1, "", {}, {}, /*flags=*/0xFFFF, /*child=*/nullptr, 783 &execve_errno)); 784 EXPECT_EQ(execve_errno, EINVAL); 785 } 786 787 // Priority consistent across calls to execve() 788 TEST(GetpriorityTest, ExecveMaintainsPriority) { 789 int prio = 16; 790 ASSERT_THAT(setpriority(PRIO_PROCESS, getpid(), prio), SyscallSucceeds()); 791 792 // To avoid trying to use negative exit values, check for 793 // 20 - prio. Since prio should always be in the range [-20, 19], 794 // this leave expected_exit_code in the range [1, 40]. 795 int expected_exit_code = 20 - prio; 796 797 // Program run (priority_execve) will exit(X) where 798 // X=getpriority(PRIO_PROCESS,0). Check that this exit value is prio. 799 CheckExec(RunfilePath(kPriorityWorkload), {RunfilePath(kPriorityWorkload)}, 800 {}, W_EXITCODE(expected_exit_code, 0), ""); 801 } 802 803 void ExecWithThread() { 804 // Used to ensure that the thread has actually started. 805 absl::Mutex mu; 806 bool started = false; 807 808 ScopedThread t([&] { 809 mu.Lock(); 810 started = true; 811 mu.Unlock(); 812 813 while (true) { 814 pause(); 815 } 816 }); 817 818 mu.LockWhen(absl::Condition(&started)); 819 mu.Unlock(); 820 821 const ExecveArray argv = {"/proc/self/exe", kExit42}; 822 const ExecveArray envv; 823 824 execve("/proc/self/exe", argv.get(), envv.get()); 825 exit(errno); 826 } 827 828 void ExecFromThread() { 829 ScopedThread t([] { 830 const ExecveArray argv = {"/proc/self/exe", kExit42}; 831 const ExecveArray envv; 832 833 execve("/proc/self/exe", argv.get(), envv.get()); 834 exit(errno); 835 }); 836 837 while (true) { 838 pause(); 839 } 840 } 841 842 bool ValidateProcCmdlineVsArgv(const int argc, const char* const* argv) { 843 auto contents_or = GetContents("/proc/self/cmdline"); 844 if (!contents_or.ok()) { 845 std::cerr << "Unable to get /proc/self/cmdline: " << contents_or.error() 846 << std::endl; 847 return false; 848 } 849 auto contents = contents_or.ValueOrDie(); 850 if (contents.back() != '\0') { 851 std::cerr << "Non-null terminated /proc/self/cmdline!" << std::endl; 852 return false; 853 } 854 contents.pop_back(); 855 std::vector<std::string> procfs_cmdline = absl::StrSplit(contents, '\0'); 856 857 if (static_cast<int>(procfs_cmdline.size()) != argc) { 858 std::cerr << "argc = " << argc << " != " << procfs_cmdline.size() 859 << std::endl; 860 return false; 861 } 862 863 for (int i = 0; i < argc; ++i) { 864 if (procfs_cmdline[i] != argv[i]) { 865 std::cerr << "Procfs command line argument " << i << " mismatch " 866 << procfs_cmdline[i] << " != " << argv[i] << std::endl; 867 return false; 868 } 869 } 870 return true; 871 } 872 873 } // namespace 874 875 } // namespace testing 876 } // namespace gvisor 877 878 int main(int argc, char** argv) { 879 // Start by validating that the stack argv is consistent with procfs. 880 if (!gvisor::testing::ValidateProcCmdlineVsArgv(argc, argv)) { 881 return 1; 882 } 883 884 // Some of these tests require no background threads, so check for them before 885 // TestInit. 886 for (int i = 0; i < argc; i++) { 887 absl::string_view arg(argv[i]); 888 889 if (arg == gvisor::testing::kExit42) { 890 return 42; 891 } 892 if (arg == gvisor::testing::kExecWithThread) { 893 gvisor::testing::ExecWithThread(); 894 return 1; 895 } 896 if (arg == gvisor::testing::kExecFromThread) { 897 gvisor::testing::ExecFromThread(); 898 return 1; 899 } 900 } 901 902 gvisor::testing::TestInit(&argc, &argv); 903 return gvisor::testing::RunAllTests(); 904 }