gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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 std::string GetShortTestTmpdir() { 207 #ifdef ANDROID 208 // Using GetAbsoluteTestTmpdir() can cause the tmp directory path to exceed 209 // the max length of the interpreter script path (127). 210 // 211 // However, existing systems that are built with the ANDROID configuration 212 // have their temp directory in a different location, and must respect the 213 // TEST_TMPDIR. 214 return GetAbsoluteTestTmpdir(); 215 #else 216 return "/tmp"; 217 #endif // ANDROID 218 } 219 220 // Everything after the path in the interpreter script is a single argument. 221 TEST(ExecTest, InterpreterScriptArgSplit) { 222 // Symlink through /tmp to ensure the path is short enough. 223 TempPath link = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateSymlinkTo( 224 GetShortTestTmpdir(), RunfilePath(kBasicWorkload))); 225 226 TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 227 GetShortTestTmpdir(), absl::StrCat("#!", link.path(), " foo bar"), 0755)); 228 229 CheckExec(script.path(), {script.path()}, {}, ArgEnvExitStatus(2, 0), 230 absl::StrCat(link.path(), "\nfoo bar\n", script.path(), "\n")); 231 } 232 233 // Original argv[0] is replaced with the script path. 234 TEST(ExecTest, InterpreterScriptArgvZero) { 235 // Symlink through /tmp to ensure the path is short enough. 236 TempPath link = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateSymlinkTo( 237 GetShortTestTmpdir(), RunfilePath(kBasicWorkload))); 238 239 TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 240 GetShortTestTmpdir(), absl::StrCat("#!", link.path()), 0755)); 241 242 CheckExec(script.path(), {"REPLACED"}, {}, ArgEnvExitStatus(1, 0), 243 absl::StrCat(link.path(), "\n", script.path(), "\n")); 244 } 245 246 // Original argv[0] is replaced with the script path, exactly as passed to 247 // execve. 248 TEST(ExecTest, InterpreterScriptArgvZeroRelative) { 249 // Symlink through /tmp to ensure the path is short enough. 250 TempPath link = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateSymlinkTo( 251 GetShortTestTmpdir(), RunfilePath(kBasicWorkload))); 252 253 TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 254 GetShortTestTmpdir(), absl::StrCat("#!", link.path()), 0755)); 255 256 auto cwd = ASSERT_NO_ERRNO_AND_VALUE(GetCWD()); 257 auto script_relative = 258 ASSERT_NO_ERRNO_AND_VALUE(GetRelativePath(cwd, script.path())); 259 260 CheckExec(script_relative, {"REPLACED"}, {}, ArgEnvExitStatus(1, 0), 261 absl::StrCat(link.path(), "\n", script_relative, "\n")); 262 } 263 264 // argv[0] is added as the script path, even if there was none. 265 TEST(ExecTest, InterpreterScriptArgvZeroAdded) { 266 // Symlink through /tmp to ensure the path is short enough. 267 TempPath link = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateSymlinkTo( 268 GetShortTestTmpdir(), RunfilePath(kBasicWorkload))); 269 270 TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 271 GetShortTestTmpdir(), absl::StrCat("#!", link.path()), 0755)); 272 273 CheckExec(script.path(), {}, {}, ArgEnvExitStatus(1, 0), 274 absl::StrCat(link.path(), "\n", script.path(), "\n")); 275 } 276 277 // A NUL byte in the script line ends parsing. 278 TEST(ExecTest, InterpreterScriptArgNUL) { 279 // Symlink through /tmp to ensure the path is short enough. 280 TempPath link = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateSymlinkTo( 281 GetShortTestTmpdir(), RunfilePath(kBasicWorkload))); 282 283 TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 284 GetShortTestTmpdir(), 285 absl::StrCat("#!", link.path(), " foo", std::string(1, '\0'), "bar"), 286 0755)); 287 288 CheckExec(script.path(), {script.path()}, {}, ArgEnvExitStatus(2, 0), 289 absl::StrCat(link.path(), "\nfoo\n", script.path(), "\n")); 290 } 291 292 // Trailing whitespace following interpreter path is ignored. 293 TEST(ExecTest, InterpreterScriptTrailingWhitespace) { 294 // Symlink through /tmp to ensure the path is short enough. 295 TempPath link = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateSymlinkTo( 296 GetShortTestTmpdir(), RunfilePath(kBasicWorkload))); 297 298 TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 299 GetShortTestTmpdir(), absl::StrCat("#!", link.path(), " \n"), 0755)); 300 301 CheckExec(script.path(), {script.path()}, {}, ArgEnvExitStatus(1, 0), 302 absl::StrCat(link.path(), "\n", script.path(), "\n")); 303 } 304 305 // Multiple whitespace characters between interpreter and arg allowed. 306 TEST(ExecTest, InterpreterScriptArgWhitespace) { 307 // Symlink through /tmp to ensure the path is short enough. 308 TempPath link = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateSymlinkTo( 309 GetShortTestTmpdir(), RunfilePath(kBasicWorkload))); 310 311 TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 312 GetShortTestTmpdir(), absl::StrCat("#!", link.path(), " foo"), 0755)); 313 314 CheckExec(script.path(), {script.path()}, {}, ArgEnvExitStatus(2, 0), 315 absl::StrCat(link.path(), "\nfoo\n", script.path(), "\n")); 316 } 317 318 TEST(ExecTest, InterpreterScriptNoPath) { 319 TempPath script = ASSERT_NO_ERRNO_AND_VALUE( 320 TempPath::CreateFileWith(GetShortTestTmpdir(), "#!\n\n", 0755)); 321 322 int execve_errno; 323 ASSERT_NO_ERRNO_AND_VALUE( 324 ForkAndExec(script.path(), {script.path()}, {}, nullptr, &execve_errno)); 325 EXPECT_EQ(execve_errno, ENOEXEC); 326 } 327 328 // AT_EXECFN is the path passed to execve. 329 TEST(ExecTest, ExecFn) { 330 // Symlink through /tmp to ensure the path is short enough. 331 TempPath link = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateSymlinkTo( 332 GetShortTestTmpdir(), RunfilePath(kStateWorkload))); 333 334 TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 335 GetShortTestTmpdir(), absl::StrCat("#!", link.path(), " PrintExecFn"), 336 0755)); 337 338 // Pass the script as a relative path and assert that is what appears in 339 // AT_EXECFN. 340 auto cwd = ASSERT_NO_ERRNO_AND_VALUE(GetCWD()); 341 auto script_relative = 342 ASSERT_NO_ERRNO_AND_VALUE(GetRelativePath(cwd, script.path())); 343 344 CheckExec(script_relative, {script_relative}, {}, ArgEnvExitStatus(0, 0), 345 absl::StrCat(script_relative, "\n")); 346 } 347 348 TEST(ExecTest, ExecName) { 349 std::string path = RunfilePath(kStateWorkload); 350 351 CheckExec(path, {path, "PrintExecName"}, {}, ArgEnvExitStatus(0, 0), 352 absl::StrCat(Basename(path).substr(0, 15), "\n")); 353 } 354 355 TEST(ExecTest, ExecNameScript) { 356 // Symlink through /tmp to ensure the path is short enough. 357 TempPath link = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateSymlinkTo( 358 GetShortTestTmpdir(), RunfilePath(kStateWorkload))); 359 360 TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 361 GetShortTestTmpdir(), absl::StrCat("#!", link.path(), " PrintExecName"), 362 0755)); 363 364 std::string script_path = script.path(); 365 366 CheckExec(script_path, {script_path}, {}, ArgEnvExitStatus(0, 0), 367 absl::StrCat(Basename(script_path).substr(0, 15), "\n")); 368 } 369 370 // execve may be called by a multithreaded process. 371 TEST(ExecTest, WithSiblingThread) { 372 CheckExec("/proc/self/exe", {"/proc/self/exe", kExecWithThread}, {}, 373 W_EXITCODE(42, 0), ""); 374 } 375 376 // execve may be called from a thread other than the leader of a multithreaded 377 // process. 378 TEST(ExecTest, FromSiblingThread) { 379 CheckExec("/proc/self/exe", {"/proc/self/exe", kExecFromThread}, {}, 380 W_EXITCODE(42, 0), ""); 381 } 382 383 TEST(ExecTest, NotFound) { 384 char* const argv[] = {nullptr}; 385 char* const envp[] = {nullptr}; 386 EXPECT_THAT(execve("/file/does/not/exist", argv, envp), 387 SyscallFailsWithErrno(ENOENT)); 388 } 389 390 TEST(ExecTest, NoExecPerm) { 391 char* const argv[] = {nullptr}; 392 char* const envp[] = {nullptr}; 393 auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 394 EXPECT_THAT(execve(f.path().c_str(), argv, envp), 395 SyscallFailsWithErrno(EACCES)); 396 } 397 398 // A signal handler we never expect to be called. 399 void SignalHandler(int signo) { 400 std::cerr << "Signal " << signo << " raised." << std::endl; 401 exit(1); 402 } 403 404 // Signal handlers are reset on execve(2), unless they have default or ignored 405 // disposition. 406 TEST(ExecStateTest, HandlerReset) { 407 struct sigaction sa; 408 sa.sa_handler = SignalHandler; 409 ASSERT_THAT(sigaction(SIGUSR1, &sa, nullptr), SyscallSucceeds()); 410 411 ExecveArray args = { 412 RunfilePath(kStateWorkload), 413 "CheckSigHandler", 414 absl::StrCat(SIGUSR1), 415 absl::StrCat(absl::Hex(reinterpret_cast<uintptr_t>(SIG_DFL))), 416 }; 417 418 CheckExec(RunfilePath(kStateWorkload), args, {}, W_EXITCODE(0, 0), ""); 419 } 420 421 // Ignored signal dispositions are not reset. 422 TEST(ExecStateTest, IgnorePreserved) { 423 struct sigaction sa; 424 sa.sa_handler = SIG_IGN; 425 ASSERT_THAT(sigaction(SIGUSR1, &sa, nullptr), SyscallSucceeds()); 426 427 ExecveArray args = { 428 RunfilePath(kStateWorkload), 429 "CheckSigHandler", 430 absl::StrCat(SIGUSR1), 431 absl::StrCat(absl::Hex(reinterpret_cast<uintptr_t>(SIG_IGN))), 432 }; 433 434 CheckExec(RunfilePath(kStateWorkload), args, {}, W_EXITCODE(0, 0), ""); 435 } 436 437 // Signal masks are not reset on exec 438 TEST(ExecStateTest, SignalMask) { 439 sigset_t s; 440 sigemptyset(&s); 441 sigaddset(&s, SIGUSR1); 442 ASSERT_THAT(sigprocmask(SIG_BLOCK, &s, nullptr), SyscallSucceeds()); 443 444 ExecveArray args = { 445 RunfilePath(kStateWorkload), 446 "CheckSigBlocked", 447 absl::StrCat(SIGUSR1), 448 }; 449 450 CheckExec(RunfilePath(kStateWorkload), args, {}, W_EXITCODE(0, 0), ""); 451 } 452 453 // itimers persist across execve. 454 // N.B. Timers created with timer_create(2) should not be preserved! 455 TEST(ExecStateTest, ItimerPreserved) { 456 // The fork in ForkAndExec clears itimers, so only set them up after fork. 457 auto setup_itimer = [] { 458 // Ignore SIGALRM, as we don't actually care about timer 459 // expirations. 460 struct sigaction sa; 461 sa.sa_handler = SIG_IGN; 462 int ret = sigaction(SIGALRM, &sa, nullptr); 463 if (ret < 0) { 464 _exit(errno); 465 } 466 467 struct itimerval itv; 468 itv.it_interval.tv_sec = 1; 469 itv.it_interval.tv_usec = 0; 470 itv.it_value.tv_sec = 1; 471 itv.it_value.tv_usec = 0; 472 ret = setitimer(ITIMER_REAL, &itv, nullptr); 473 if (ret < 0) { 474 _exit(errno); 475 } 476 }; 477 478 std::string filename = RunfilePath(kStateWorkload); 479 ExecveArray argv = { 480 filename, 481 "CheckItimerEnabled", 482 absl::StrCat(ITIMER_REAL), 483 }; 484 485 pid_t child; 486 int execve_errno; 487 auto kill = ASSERT_NO_ERRNO_AND_VALUE( 488 ForkAndExec(filename, argv, {}, setup_itimer, &child, &execve_errno)); 489 ASSERT_EQ(0, execve_errno); 490 491 int status; 492 ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), SyscallSucceeds()); 493 EXPECT_EQ(0, status); 494 495 // Process cleanup no longer needed. 496 kill.Release(); 497 } 498 499 TEST(ProcSelfExe, ChangesAcrossExecve) { 500 // See exec_proc_exe_workload for more details. We simply 501 // assert that the /proc/self/exe link changes across execve. 502 CheckExec(RunfilePath(kProcExeWorkload), 503 {RunfilePath(kProcExeWorkload), 504 ASSERT_NO_ERRNO_AND_VALUE(ProcessExePath(getpid()))}, 505 {}, W_EXITCODE(0, 0), ""); 506 } 507 508 TEST(ExecTest, CloexecNormalFile) { 509 TempPath tempFile = ASSERT_NO_ERRNO_AND_VALUE( 510 TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), "bar", 0755)); 511 const FileDescriptor fd_closed_on_exec = 512 ASSERT_NO_ERRNO_AND_VALUE(Open(tempFile.path(), O_RDONLY | O_CLOEXEC)); 513 514 CheckExec(RunfilePath(kAssertClosedWorkload), 515 {RunfilePath(kAssertClosedWorkload), 516 absl::StrCat(fd_closed_on_exec.get())}, 517 {}, W_EXITCODE(0, 0), ""); 518 519 // The assert closed workload exits with code 2 if the file still exists. We 520 // can use this to do a negative test. 521 const FileDescriptor fd_open_on_exec = 522 ASSERT_NO_ERRNO_AND_VALUE(Open(tempFile.path(), O_RDONLY)); 523 524 CheckExec( 525 RunfilePath(kAssertClosedWorkload), 526 {RunfilePath(kAssertClosedWorkload), absl::StrCat(fd_open_on_exec.get())}, 527 {}, W_EXITCODE(2, 0), ""); 528 } 529 530 TEST(ExecTest, CloexecEventfd) { 531 int efd; 532 ASSERT_THAT(efd = eventfd(0, EFD_CLOEXEC), SyscallSucceeds()); 533 FileDescriptor fd(efd); 534 535 CheckExec(RunfilePath(kAssertClosedWorkload), 536 {RunfilePath(kAssertClosedWorkload), absl::StrCat(fd.get())}, {}, 537 W_EXITCODE(0, 0), ""); 538 } 539 540 constexpr int kLinuxMaxSymlinks = 40; 541 542 TEST(ExecTest, SymlinkLimitExceeded) { 543 std::string path = RunfilePath(kBasicWorkload); 544 545 // Hold onto TempPath objects so they are not destructed prematurely. 546 std::vector<TempPath> symlinks; 547 for (int i = 0; i < kLinuxMaxSymlinks + 1; i++) { 548 symlinks.push_back(ASSERT_NO_ERRNO_AND_VALUE( 549 TempPath::CreateSymlinkTo(GetAbsoluteTestTmpdir(), path))); 550 path = symlinks[i].path(); 551 } 552 553 int execve_errno; 554 ASSERT_NO_ERRNO_AND_VALUE( 555 ForkAndExec(path, {path}, {}, /*child=*/nullptr, &execve_errno)); 556 EXPECT_EQ(execve_errno, ELOOP); 557 } 558 559 TEST(ExecTest, SymlinkLimitRefreshedForInterpreter) { 560 std::string tmp_dir = GetAbsoluteTestTmpdir(); 561 std::string interpreter_path = "/bin/echo"; 562 TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 563 tmp_dir, absl::StrCat("#!", interpreter_path), 0755)); 564 std::string script_path = script.path(); 565 566 // Hold onto TempPath objects so they are not destructed prematurely. 567 std::vector<TempPath> interpreter_symlinks; 568 std::vector<TempPath> script_symlinks; 569 // Replace both the interpreter and script paths with symlink chains of just 570 // over half the symlink limit each; this is the minimum required to test that 571 // the symlink limit applies separately to each traversal, while tolerating 572 // some symlinks in the resolution of (the original) interpreter_path and 573 // script_path. 574 for (int i = 0; i < (kLinuxMaxSymlinks / 2) + 1; i++) { 575 interpreter_symlinks.push_back(ASSERT_NO_ERRNO_AND_VALUE( 576 TempPath::CreateSymlinkTo(tmp_dir, interpreter_path))); 577 interpreter_path = interpreter_symlinks[i].path(); 578 script_symlinks.push_back(ASSERT_NO_ERRNO_AND_VALUE( 579 TempPath::CreateSymlinkTo(tmp_dir, script_path))); 580 script_path = script_symlinks[i].path(); 581 } 582 583 CheckExec(script_path, {script_path}, {}, ArgEnvExitStatus(0, 0), ""); 584 } 585 586 TEST(ExecveatTest, BasicWithFDCWD) { 587 std::string path = RunfilePath(kBasicWorkload); 588 CheckExecveat(AT_FDCWD, path, {path}, {}, /*flags=*/0, ArgEnvExitStatus(0, 0), 589 absl::StrCat(path, "\n")); 590 } 591 592 TEST(ExecveatTest, Basic) { 593 std::string absolute_path = RunfilePath(kBasicWorkload); 594 std::string parent_dir = std::string(Dirname(absolute_path)); 595 std::string base = std::string(Basename(absolute_path)); 596 const FileDescriptor dirfd = 597 ASSERT_NO_ERRNO_AND_VALUE(Open(parent_dir, O_DIRECTORY)); 598 599 CheckExecveat(dirfd.get(), base, {absolute_path}, {}, /*flags=*/0, 600 ArgEnvExitStatus(0, 0), absl::StrCat(absolute_path, "\n")); 601 } 602 603 TEST(ExecveatTest, FDNotADirectory) { 604 std::string absolute_path = RunfilePath(kBasicWorkload); 605 std::string base = std::string(Basename(absolute_path)); 606 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(absolute_path, 0)); 607 608 int execve_errno; 609 ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(fd.get(), base, {absolute_path}, {}, 610 /*flags=*/0, /*child=*/nullptr, 611 &execve_errno)); 612 EXPECT_EQ(execve_errno, ENOTDIR); 613 } 614 615 TEST(ExecveatTest, AbsolutePathWithFDCWD) { 616 std::string path = RunfilePath(kBasicWorkload); 617 CheckExecveat(AT_FDCWD, path, {path}, {}, ArgEnvExitStatus(0, 0), 0, 618 absl::StrCat(path, "\n")); 619 } 620 621 TEST(ExecveatTest, AbsolutePath) { 622 std::string path = RunfilePath(kBasicWorkload); 623 // File descriptor should be ignored when an absolute path is given. 624 const int32_t badFD = -1; 625 CheckExecveat(badFD, path, {path}, {}, ArgEnvExitStatus(0, 0), 0, 626 absl::StrCat(path, "\n")); 627 } 628 629 TEST(ExecveatTest, EmptyPathBasic) { 630 std::string path = RunfilePath(kBasicWorkload); 631 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_PATH)); 632 633 CheckExecveat(fd.get(), "", {path}, {}, AT_EMPTY_PATH, ArgEnvExitStatus(0, 0), 634 absl::StrCat(path, "\n")); 635 } 636 637 TEST(ExecveatTest, EmptyPathWithDirFD) { 638 std::string path = RunfilePath(kBasicWorkload); 639 std::string parent_dir = std::string(Dirname(path)); 640 const FileDescriptor dirfd = 641 ASSERT_NO_ERRNO_AND_VALUE(Open(parent_dir, O_DIRECTORY)); 642 643 int execve_errno; 644 ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(dirfd.get(), "", {path}, {}, 645 AT_EMPTY_PATH, 646 /*child=*/nullptr, &execve_errno)); 647 EXPECT_EQ(execve_errno, EACCES); 648 } 649 650 TEST(ExecveatTest, EmptyPathWithoutEmptyPathFlag) { 651 std::string path = RunfilePath(kBasicWorkload); 652 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_PATH)); 653 654 int execve_errno; 655 ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat( 656 fd.get(), "", {path}, {}, /*flags=*/0, /*child=*/nullptr, &execve_errno)); 657 EXPECT_EQ(execve_errno, ENOENT); 658 } 659 660 TEST(ExecveatTest, AbsolutePathWithEmptyPathFlag) { 661 std::string path = RunfilePath(kBasicWorkload); 662 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_PATH)); 663 664 CheckExecveat(fd.get(), path, {path}, {}, AT_EMPTY_PATH, 665 ArgEnvExitStatus(0, 0), absl::StrCat(path, "\n")); 666 } 667 668 TEST(ExecveatTest, RelativePathWithEmptyPathFlag) { 669 std::string absolute_path = RunfilePath(kBasicWorkload); 670 std::string parent_dir = std::string(Dirname(absolute_path)); 671 std::string base = std::string(Basename(absolute_path)); 672 const FileDescriptor dirfd = 673 ASSERT_NO_ERRNO_AND_VALUE(Open(parent_dir, O_DIRECTORY)); 674 675 CheckExecveat(dirfd.get(), base, {absolute_path}, {}, AT_EMPTY_PATH, 676 ArgEnvExitStatus(0, 0), absl::StrCat(absolute_path, "\n")); 677 } 678 679 TEST(ExecveatTest, SymlinkNoFollowWithRelativePath) { 680 std::string parent_dir = GetAbsoluteTestTmpdir(); 681 TempPath link = ASSERT_NO_ERRNO_AND_VALUE( 682 TempPath::CreateSymlinkTo(parent_dir, RunfilePath(kBasicWorkload))); 683 const FileDescriptor dirfd = 684 ASSERT_NO_ERRNO_AND_VALUE(Open(parent_dir, O_DIRECTORY)); 685 std::string base = std::string(Basename(link.path())); 686 687 int execve_errno; 688 ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(dirfd.get(), base, {base}, {}, 689 AT_SYMLINK_NOFOLLOW, 690 /*child=*/nullptr, &execve_errno)); 691 EXPECT_EQ(execve_errno, ELOOP); 692 } 693 694 TEST(ExecveatTest, UnshareFiles) { 695 TempPath tempFile = ASSERT_NO_ERRNO_AND_VALUE( 696 TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), "bar", 0755)); 697 const FileDescriptor fd_closed_on_exec = 698 ASSERT_NO_ERRNO_AND_VALUE(Open(tempFile.path(), O_RDONLY | O_CLOEXEC)); 699 700 ExecveArray argv = {"test"}; 701 ExecveArray envp; 702 std::string child_path = RunfilePath(kBasicWorkload); 703 pid_t child = 704 syscall(__NR_clone, SIGCHLD | CLONE_VFORK | CLONE_FILES, 0, 0, 0, 0); 705 if (child == 0) { 706 execve(child_path.c_str(), argv.get(), envp.get()); 707 _exit(1); 708 } 709 ASSERT_THAT(child, SyscallSucceeds()); 710 711 int status; 712 ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), SyscallSucceeds()); 713 EXPECT_EQ(status, 0); 714 715 struct stat st; 716 EXPECT_THAT(fstat(fd_closed_on_exec.get(), &st), SyscallSucceeds()); 717 } 718 719 TEST(ExecveatTest, SymlinkNoFollowWithAbsolutePath) { 720 std::string parent_dir = GetAbsoluteTestTmpdir(); 721 TempPath link = ASSERT_NO_ERRNO_AND_VALUE( 722 TempPath::CreateSymlinkTo(parent_dir, RunfilePath(kBasicWorkload))); 723 std::string path = link.path(); 724 725 int execve_errno; 726 ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(AT_FDCWD, path, {path}, {}, 727 AT_SYMLINK_NOFOLLOW, 728 /*child=*/nullptr, &execve_errno)); 729 EXPECT_EQ(execve_errno, ELOOP); 730 } 731 732 TEST(ExecveatTest, SymlinkNoFollowAndEmptyPath) { 733 TempPath link = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateSymlinkTo( 734 GetAbsoluteTestTmpdir(), RunfilePath(kBasicWorkload))); 735 std::string path = link.path(); 736 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, 0)); 737 738 CheckExecveat(fd.get(), "", {path}, {}, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, 739 ArgEnvExitStatus(0, 0), absl::StrCat(path, "\n")); 740 } 741 742 TEST(ExecveatTest, SymlinkNoFollowIgnoreSymlinkAncestor) { 743 TempPath parent_link = ASSERT_NO_ERRNO_AND_VALUE( 744 TempPath::CreateSymlinkTo(GetAbsoluteTestTmpdir(), "/bin")); 745 std::string path_with_symlink = JoinPath(parent_link.path(), "echo"); 746 747 CheckExecveat(AT_FDCWD, path_with_symlink, {path_with_symlink}, {}, 748 AT_SYMLINK_NOFOLLOW, ArgEnvExitStatus(0, 0), ""); 749 } 750 751 TEST(ExecveatTest, SymlinkNoFollowWithNormalFile) { 752 const FileDescriptor dirfd = 753 ASSERT_NO_ERRNO_AND_VALUE(Open("/bin", O_DIRECTORY)); 754 755 CheckExecveat(dirfd.get(), "echo", {"echo"}, {}, AT_SYMLINK_NOFOLLOW, 756 ArgEnvExitStatus(0, 0), ""); 757 } 758 759 TEST(ExecveatTest, BasicWithCloexecFD) { 760 std::string path = RunfilePath(kBasicWorkload); 761 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_CLOEXEC)); 762 763 CheckExecveat(fd.get(), "", {path}, {}, AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH, 764 ArgEnvExitStatus(0, 0), absl::StrCat(path, "\n")); 765 } 766 767 TEST(ExecveatTest, InterpreterScriptWithCloexecFD) { 768 std::string path = RunfilePath(kExitScript); 769 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_CLOEXEC)); 770 771 int execve_errno; 772 ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(fd.get(), "", {path}, {}, 773 AT_EMPTY_PATH, /*child=*/nullptr, 774 &execve_errno)); 775 EXPECT_EQ(execve_errno, ENOENT); 776 } 777 778 TEST(ExecveatTest, InterpreterScriptWithCloexecDirFD) { 779 std::string absolute_path = RunfilePath(kExitScript); 780 std::string parent_dir = std::string(Dirname(absolute_path)); 781 std::string base = std::string(Basename(absolute_path)); 782 const FileDescriptor dirfd = 783 ASSERT_NO_ERRNO_AND_VALUE(Open(parent_dir, O_CLOEXEC | O_DIRECTORY)); 784 785 int execve_errno; 786 ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(dirfd.get(), base, {base}, {}, 787 /*flags=*/0, /*child=*/nullptr, 788 &execve_errno)); 789 EXPECT_EQ(execve_errno, ENOENT); 790 } 791 792 TEST(ExecveatTest, InvalidFlags) { 793 int execve_errno; 794 ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat( 795 /*dirfd=*/-1, "", {}, {}, /*flags=*/0xFFFF, /*child=*/nullptr, 796 &execve_errno)); 797 EXPECT_EQ(execve_errno, EINVAL); 798 } 799 800 // Priority consistent across calls to execve() 801 TEST(GetpriorityTest, ExecveMaintainsPriority) { 802 int prio = 16; 803 ASSERT_THAT(setpriority(PRIO_PROCESS, getpid(), prio), SyscallSucceeds()); 804 805 // To avoid trying to use negative exit values, check for 806 // 20 - prio. Since prio should always be in the range [-20, 19], 807 // this leave expected_exit_code in the range [1, 40]. 808 int expected_exit_code = 20 - prio; 809 810 // Program run (priority_execve) will exit(X) where 811 // X=getpriority(PRIO_PROCESS,0). Check that this exit value is prio. 812 CheckExec(RunfilePath(kPriorityWorkload), {RunfilePath(kPriorityWorkload)}, 813 {}, W_EXITCODE(expected_exit_code, 0), ""); 814 } 815 816 void ExecWithThread() { 817 // Used to ensure that the thread has actually started. 818 absl::Mutex mu; 819 bool started = false; 820 821 ScopedThread t([&] { 822 mu.Lock(); 823 started = true; 824 mu.Unlock(); 825 826 while (true) { 827 pause(); 828 } 829 }); 830 831 mu.LockWhen(absl::Condition(&started)); 832 mu.Unlock(); 833 834 const ExecveArray argv = {"/proc/self/exe", kExit42}; 835 const ExecveArray envv; 836 837 execve("/proc/self/exe", argv.get(), envv.get()); 838 exit(errno); 839 } 840 841 void ExecFromThread() { 842 ScopedThread t([] { 843 const ExecveArray argv = {"/proc/self/exe", kExit42}; 844 const ExecveArray envv; 845 846 execve("/proc/self/exe", argv.get(), envv.get()); 847 exit(errno); 848 }); 849 850 while (true) { 851 pause(); 852 } 853 } 854 855 bool ValidateProcCmdlineVsArgv(const int argc, const char* const* argv) { 856 auto contents_or = GetContents("/proc/self/cmdline"); 857 if (!contents_or.ok()) { 858 std::cerr << "Unable to get /proc/self/cmdline: " << contents_or.error() 859 << std::endl; 860 return false; 861 } 862 auto contents = contents_or.ValueOrDie(); 863 if (contents.back() != '\0') { 864 std::cerr << "Non-null terminated /proc/self/cmdline!" << std::endl; 865 return false; 866 } 867 contents.pop_back(); 868 std::vector<std::string> procfs_cmdline = absl::StrSplit(contents, '\0'); 869 870 if (static_cast<int>(procfs_cmdline.size()) != argc) { 871 std::cerr << "argc = " << argc << " != " << procfs_cmdline.size() 872 << std::endl; 873 return false; 874 } 875 876 for (int i = 0; i < argc; ++i) { 877 if (procfs_cmdline[i] != argv[i]) { 878 std::cerr << "Procfs command line argument " << i << " mismatch " 879 << procfs_cmdline[i] << " != " << argv[i] << std::endl; 880 return false; 881 } 882 } 883 return true; 884 } 885 886 } // namespace 887 888 } // namespace testing 889 } // namespace gvisor 890 891 int main(int argc, char** argv) { 892 // Start by validating that the stack argv is consistent with procfs. 893 if (!gvisor::testing::ValidateProcCmdlineVsArgv(argc, argv)) { 894 return 1; 895 } 896 897 // Some of these tests require no background threads, so check for them before 898 // TestInit. 899 for (int i = 0; i < argc; i++) { 900 absl::string_view arg(argv[i]); 901 902 if (arg == gvisor::testing::kExit42) { 903 return 42; 904 } 905 if (arg == gvisor::testing::kExecWithThread) { 906 gvisor::testing::ExecWithThread(); 907 return 1; 908 } 909 if (arg == gvisor::testing::kExecFromThread) { 910 gvisor::testing::ExecFromThread(); 911 return 1; 912 } 913 } 914 915 gvisor::testing::TestInit(&argc, &argv); 916 return gvisor::testing::RunAllTests(); 917 }