Deflake exec test.

- Only use MAXSYMLINKS/2+1 symlinks for each of the interpreter and script
  paths in SymlinkLimitRefreshedForInterpreter to tolerate cases where the
  original paths (/tmp, /bin, or /bin/echo) themselves contain symlinks.

- Ensure that UnshareFiles performs execve immediately after clone(CLONE_VFORK)
  (no heap allocation for ExecveArray/RunfilesPath).

- Use lstat() rather than stat() for the existence check in fs_util's Exists;
  the latter will fail if the symlink target does not exist, even if the
  symlink does.

PiperOrigin-RevId: 320110156
This commit is contained in:
Jamie Liu 2020-07-07 19:44:03 -07:00 committed by gVisor bot
parent 76c7bc51b7
commit 5e05950c1c
4 changed files with 22 additions and 14 deletions

View File

@ -553,7 +553,12 @@ TEST(ExecTest, SymlinkLimitRefreshedForInterpreter) {
// Hold onto TempPath objects so they are not destructed prematurely.
std::vector<TempPath> interpreter_symlinks;
std::vector<TempPath> script_symlinks;
for (int i = 0; i < kLinuxMaxSymlinks; i++) {
// Replace both the interpreter and script paths with symlink chains of just
// over half the symlink limit each; this is the minimum required to test that
// the symlink limit applies separately to each traversal, while tolerating
// some symlinks in the resolution of (the original) interpreter_path and
// script_path.
for (int i = 0; i < (kLinuxMaxSymlinks / 2) + 1; i++) {
interpreter_symlinks.push_back(ASSERT_NO_ERRNO_AND_VALUE(
TempPath::CreateSymlinkTo(tmp_dir, interpreter_path)));
interpreter_path = interpreter_symlinks[i].path();
@ -679,18 +684,16 @@ TEST(ExecveatTest, UnshareFiles) {
const FileDescriptor fd_closed_on_exec =
ASSERT_NO_ERRNO_AND_VALUE(Open(tempFile.path(), O_RDONLY | O_CLOEXEC));
pid_t child;
EXPECT_THAT(child = syscall(__NR_clone, SIGCHLD | CLONE_VFORK | CLONE_FILES,
0, 0, 0, 0),
SyscallSucceeds());
ExecveArray argv = {"test"};
ExecveArray envp;
std::string child_path = RunfilePath(kBasicWorkload);
pid_t child =
syscall(__NR_clone, SIGCHLD | CLONE_VFORK | CLONE_FILES, 0, 0, 0, 0);
if (child == 0) {
ExecveArray argv = {"test"};
ExecveArray envp;
ASSERT_THAT(
execve(RunfilePath(kBasicWorkload).c_str(), argv.get(), envp.get()),
SyscallSucceeds());
execve(child_path.c_str(), argv.get(), envp.get());
_exit(1);
}
ASSERT_THAT(child, SyscallSucceeds());
int status;
ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), SyscallSucceeds());

View File

@ -125,12 +125,12 @@ PosixErrorOr<struct stat> Fstat(int fd) {
PosixErrorOr<bool> Exists(absl::string_view path) {
struct stat stat_buf;
int res = stat(std::string(path).c_str(), &stat_buf);
int res = lstat(std::string(path).c_str(), &stat_buf);
if (res < 0) {
if (errno == ENOENT) {
return false;
}
return PosixError(errno, absl::StrCat("stat ", path));
return PosixError(errno, absl::StrCat("lstat ", path));
}
return true;
}

View File

@ -44,9 +44,14 @@ PosixErrorOr<std::string> GetCWD();
// can't be determined.
PosixErrorOr<bool> Exists(absl::string_view path);
// Returns a stat structure for the given path or an error.
// Returns a stat structure for the given path or an error. If the path
// represents a symlink, it will be traversed.
PosixErrorOr<struct stat> Stat(absl::string_view path);
// Returns a stat structure for the given path or an error. If the path
// represents a symlink, it will not be traversed.
PosixErrorOr<struct stat> Lstat(absl::string_view path);
// Returns a stat struct for the given fd.
PosixErrorOr<struct stat> Fstat(int fd);

View File

@ -56,7 +56,7 @@ void TryDeleteRecursively(std::string const& path) {
if (undeleted_dirs || undeleted_files || !status.ok()) {
std::cerr << path << ": failed to delete " << undeleted_dirs
<< " directories and " << undeleted_files
<< " files: " << status;
<< " files: " << status << std::endl;
}
}
}