mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Remove side effect from test cases
Individual test cases must not rely on being executed in a clean environment. PiperOrigin-RevId: 354604389
This commit is contained in:
committed by
gVisor bot
parent
5e2edfb872
commit
fdbfd447a0
@@ -482,7 +482,9 @@ cc_binary(
|
||||
"//test/util:fs_util",
|
||||
"@com_google_absl//absl/strings",
|
||||
gtest,
|
||||
"//test/util:logging",
|
||||
"//test/util:mount_util",
|
||||
"//test/util:multiprocess_util",
|
||||
"//test/util:temp_path",
|
||||
"//test/util:test_main",
|
||||
"//test/util:test_util",
|
||||
@@ -672,6 +674,7 @@ cc_binary(
|
||||
gtest,
|
||||
"//test/util:logging",
|
||||
"//test/util:memory_util",
|
||||
"//test/util:multiprocess_util",
|
||||
"//test/util:signal_util",
|
||||
"//test/util:test_main",
|
||||
"//test/util:test_util",
|
||||
@@ -3827,6 +3830,8 @@ cc_binary(
|
||||
"@com_google_absl//absl/flags:flag",
|
||||
"@com_google_absl//absl/strings",
|
||||
gtest,
|
||||
"//test/util:cleanup",
|
||||
"//test/util:multiprocess_util",
|
||||
"//test/util:posix_error",
|
||||
"//test/util:test_main",
|
||||
"//test/util:test_util",
|
||||
@@ -4082,6 +4087,7 @@ cc_binary(
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/strings:str_format",
|
||||
gtest,
|
||||
"//test/util:cleanup",
|
||||
"//test/util:test_main",
|
||||
"//test/util:test_util",
|
||||
],
|
||||
|
||||
+189
-176
@@ -32,7 +32,9 @@
|
||||
#include "test/util/cleanup.h"
|
||||
#include "test/util/file_descriptor.h"
|
||||
#include "test/util/fs_util.h"
|
||||
#include "test/util/logging.h"
|
||||
#include "test/util/mount_util.h"
|
||||
#include "test/util/multiprocess_util.h"
|
||||
#include "test/util/temp_path.h"
|
||||
#include "test/util/test_util.h"
|
||||
|
||||
@@ -47,17 +49,20 @@ namespace {
|
||||
TEST(ChrootTest, Success) {
|
||||
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT)));
|
||||
|
||||
auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
|
||||
EXPECT_THAT(chroot(temp_dir.path().c_str()), SyscallSucceeds());
|
||||
const auto rest = [] {
|
||||
auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
|
||||
TEST_CHECK_SUCCESS(chroot(temp_dir.path().c_str()));
|
||||
};
|
||||
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
|
||||
}
|
||||
|
||||
TEST(ChrootTest, PermissionDenied) {
|
||||
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT)));
|
||||
|
||||
// CAP_DAC_READ_SEARCH and CAP_DAC_OVERRIDE may override Execute permission on
|
||||
// directories.
|
||||
ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false));
|
||||
ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));
|
||||
// CAP_DAC_READ_SEARCH and CAP_DAC_OVERRIDE may override Execute permission
|
||||
// on directories.
|
||||
AutoCapability cap_search(CAP_DAC_READ_SEARCH, false);
|
||||
AutoCapability cap_override(CAP_DAC_OVERRIDE, false);
|
||||
|
||||
auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(
|
||||
TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0666 /* mode */));
|
||||
@@ -78,8 +83,10 @@ TEST(ChrootTest, NotExist) {
|
||||
}
|
||||
|
||||
TEST(ChrootTest, WithoutCapability) {
|
||||
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SETPCAP)));
|
||||
|
||||
// Unset CAP_SYS_CHROOT.
|
||||
ASSERT_NO_ERRNO(SetCapability(CAP_SYS_CHROOT, false));
|
||||
AutoCapability cap(CAP_SYS_CHROOT, false);
|
||||
|
||||
auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
|
||||
EXPECT_THAT(chroot(temp_dir.path().c_str()), SyscallFailsWithErrno(EPERM));
|
||||
@@ -97,51 +104,53 @@ TEST(ChrootTest, CreatesNewRoot) {
|
||||
auto file_in_new_root =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(new_root.path()));
|
||||
|
||||
// chroot into new_root.
|
||||
ASSERT_THAT(chroot(new_root.path().c_str()), SyscallSucceeds());
|
||||
const auto rest = [&] {
|
||||
// chroot into new_root.
|
||||
TEST_CHECK_SUCCESS(chroot(new_root.path().c_str()));
|
||||
|
||||
// getcwd should return "(unreachable)" followed by the initial_cwd.
|
||||
char cwd[1024];
|
||||
ASSERT_THAT(syscall(__NR_getcwd, cwd, sizeof(cwd)), SyscallSucceeds());
|
||||
std::string expected_cwd = "(unreachable)";
|
||||
expected_cwd += initial_cwd;
|
||||
EXPECT_STREQ(cwd, expected_cwd.c_str());
|
||||
// getcwd should return "(unreachable)" followed by the initial_cwd.
|
||||
char cwd[1024];
|
||||
TEST_CHECK_SUCCESS(syscall(__NR_getcwd, cwd, sizeof(cwd)));
|
||||
std::string expected_cwd = "(unreachable)";
|
||||
expected_cwd += initial_cwd;
|
||||
TEST_CHECK(strcmp(cwd, expected_cwd.c_str()) == 0);
|
||||
|
||||
// Should not be able to stat file by its full path.
|
||||
struct stat statbuf;
|
||||
EXPECT_THAT(stat(file_in_new_root.path().c_str(), &statbuf),
|
||||
SyscallFailsWithErrno(ENOENT));
|
||||
// Should not be able to stat file by its full path.
|
||||
struct stat statbuf;
|
||||
TEST_CHECK_ERRNO(stat(file_in_new_root.path().c_str(), &statbuf), ENOENT);
|
||||
|
||||
// Should be able to stat file at new rooted path.
|
||||
auto basename = std::string(Basename(file_in_new_root.path()));
|
||||
auto rootedFile = "/" + basename;
|
||||
ASSERT_THAT(stat(rootedFile.c_str(), &statbuf), SyscallSucceeds());
|
||||
// Should be able to stat file at new rooted path.
|
||||
auto basename = std::string(Basename(file_in_new_root.path()));
|
||||
auto rootedFile = "/" + basename;
|
||||
TEST_CHECK_SUCCESS(stat(rootedFile.c_str(), &statbuf));
|
||||
|
||||
// Should be able to stat cwd at '.' even though it's outside root.
|
||||
ASSERT_THAT(stat(".", &statbuf), SyscallSucceeds());
|
||||
// Should be able to stat cwd at '.' even though it's outside root.
|
||||
TEST_CHECK_SUCCESS(stat(".", &statbuf));
|
||||
|
||||
// chdir into new root.
|
||||
ASSERT_THAT(chdir("/"), SyscallSucceeds());
|
||||
// chdir into new root.
|
||||
TEST_CHECK_SUCCESS(chdir("/"));
|
||||
|
||||
// getcwd should return "/".
|
||||
EXPECT_THAT(syscall(__NR_getcwd, cwd, sizeof(cwd)), SyscallSucceeds());
|
||||
EXPECT_STREQ(cwd, "/");
|
||||
// getcwd should return "/".
|
||||
TEST_CHECK_SUCCESS(syscall(__NR_getcwd, cwd, sizeof(cwd)));
|
||||
TEST_CHECK_SUCCESS(strcmp(cwd, "/") == 0);
|
||||
|
||||
// Statting '.', '..', '/', and '/..' all return the same dev and inode.
|
||||
struct stat statbuf_dot;
|
||||
ASSERT_THAT(stat(".", &statbuf_dot), SyscallSucceeds());
|
||||
struct stat statbuf_dotdot;
|
||||
ASSERT_THAT(stat("..", &statbuf_dotdot), SyscallSucceeds());
|
||||
EXPECT_EQ(statbuf_dot.st_dev, statbuf_dotdot.st_dev);
|
||||
EXPECT_EQ(statbuf_dot.st_ino, statbuf_dotdot.st_ino);
|
||||
struct stat statbuf_slash;
|
||||
ASSERT_THAT(stat("/", &statbuf_slash), SyscallSucceeds());
|
||||
EXPECT_EQ(statbuf_dot.st_dev, statbuf_slash.st_dev);
|
||||
EXPECT_EQ(statbuf_dot.st_ino, statbuf_slash.st_ino);
|
||||
struct stat statbuf_slashdotdot;
|
||||
ASSERT_THAT(stat("/..", &statbuf_slashdotdot), SyscallSucceeds());
|
||||
EXPECT_EQ(statbuf_dot.st_dev, statbuf_slashdotdot.st_dev);
|
||||
EXPECT_EQ(statbuf_dot.st_ino, statbuf_slashdotdot.st_ino);
|
||||
// Statting '.', '..', '/', and '/..' all return the same dev and inode.
|
||||
struct stat statbuf_dot;
|
||||
TEST_CHECK_SUCCESS(stat(".", &statbuf_dot));
|
||||
struct stat statbuf_dotdot;
|
||||
TEST_CHECK_SUCCESS(stat("..", &statbuf_dotdot));
|
||||
TEST_CHECK(statbuf_dot.st_dev == statbuf_dotdot.st_dev);
|
||||
TEST_CHECK(statbuf_dot.st_ino == statbuf_dotdot.st_ino);
|
||||
struct stat statbuf_slash;
|
||||
TEST_CHECK_SUCCESS(stat("/", &statbuf_slash));
|
||||
TEST_CHECK(statbuf_dot.st_dev == statbuf_slash.st_dev);
|
||||
TEST_CHECK(statbuf_dot.st_ino == statbuf_slash.st_ino);
|
||||
struct stat statbuf_slashdotdot;
|
||||
TEST_CHECK_SUCCESS(stat("/..", &statbuf_slashdotdot));
|
||||
TEST_CHECK(statbuf_dot.st_dev == statbuf_slashdotdot.st_dev);
|
||||
TEST_CHECK(statbuf_dot.st_ino == statbuf_slashdotdot.st_ino);
|
||||
};
|
||||
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
|
||||
}
|
||||
|
||||
TEST(ChrootTest, DotDotFromOpenFD) {
|
||||
@@ -152,18 +161,20 @@ TEST(ChrootTest, DotDotFromOpenFD) {
|
||||
Open(dir_outside_root.path(), O_RDONLY | O_DIRECTORY));
|
||||
auto new_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
|
||||
|
||||
// chroot into new_root.
|
||||
ASSERT_THAT(chroot(new_root.path().c_str()), SyscallSucceeds());
|
||||
const auto rest = [&] {
|
||||
// chroot into new_root.
|
||||
TEST_CHECK_SUCCESS(chroot(new_root.path().c_str()));
|
||||
|
||||
// openat on fd with path .. will succeed.
|
||||
int other_fd;
|
||||
ASSERT_THAT(other_fd = openat(fd.get(), "..", O_RDONLY), SyscallSucceeds());
|
||||
EXPECT_THAT(close(other_fd), SyscallSucceeds());
|
||||
// openat on fd with path .. will succeed.
|
||||
int other_fd;
|
||||
TEST_CHECK_SUCCESS(other_fd = openat(fd.get(), "..", O_RDONLY));
|
||||
TEST_CHECK_SUCCESS(close(other_fd));
|
||||
|
||||
// getdents on fd should not error.
|
||||
char buf[1024];
|
||||
ASSERT_THAT(syscall(SYS_getdents64, fd.get(), buf, sizeof(buf)),
|
||||
SyscallSucceeds());
|
||||
// getdents on fd should not error.
|
||||
char buf[1024];
|
||||
TEST_CHECK_SUCCESS(syscall(SYS_getdents64, fd.get(), buf, sizeof(buf)));
|
||||
};
|
||||
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
|
||||
}
|
||||
|
||||
// Test that link resolution in a chroot can escape the root by following an
|
||||
@@ -179,24 +190,27 @@ TEST(ChrootTest, ProcFdLinkResolutionInChroot) {
|
||||
const FileDescriptor proc_fd = ASSERT_NO_ERRNO_AND_VALUE(
|
||||
Open("/proc", O_DIRECTORY | O_RDONLY | O_CLOEXEC));
|
||||
|
||||
auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
|
||||
ASSERT_THAT(chroot(temp_dir.path().c_str()), SyscallSucceeds());
|
||||
const auto rest = [&] {
|
||||
auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
|
||||
TEST_CHECK_SUCCESS(chroot(temp_dir.path().c_str()));
|
||||
|
||||
// Opening relative to an already open fd to a node outside the chroot works.
|
||||
const FileDescriptor proc_self_fd = ASSERT_NO_ERRNO_AND_VALUE(
|
||||
OpenAt(proc_fd.get(), "self/fd", O_DIRECTORY | O_RDONLY | O_CLOEXEC));
|
||||
// Opening relative to an already open fd to a node outside the chroot
|
||||
// works.
|
||||
const FileDescriptor proc_self_fd = TEST_CHECK_NO_ERRNO_AND_VALUE(
|
||||
OpenAt(proc_fd.get(), "self/fd", O_DIRECTORY | O_RDONLY | O_CLOEXEC));
|
||||
|
||||
// Proc fd symlinks can escape the chroot if the fd the symlink refers to
|
||||
// refers to an object outside the chroot.
|
||||
struct stat s = {};
|
||||
EXPECT_THAT(
|
||||
fstatat(proc_self_fd.get(), absl::StrCat(fd.get()).c_str(), &s, 0),
|
||||
SyscallSucceeds());
|
||||
// Proc fd symlinks can escape the chroot if the fd the symlink refers to
|
||||
// refers to an object outside the chroot.
|
||||
struct stat s = {};
|
||||
TEST_CHECK_SUCCESS(
|
||||
fstatat(proc_self_fd.get(), absl::StrCat(fd.get()).c_str(), &s, 0));
|
||||
|
||||
// Try to stat the stdin fd. Internally, this is handled differently from a
|
||||
// proc fd entry pointing to a file, since stdin is backed by a host fd, and
|
||||
// isn't a walkable path on the filesystem inside the sandbox.
|
||||
EXPECT_THAT(fstatat(proc_self_fd.get(), "0", &s, 0), SyscallSucceeds());
|
||||
// Try to stat the stdin fd. Internally, this is handled differently from a
|
||||
// proc fd entry pointing to a file, since stdin is backed by a host fd, and
|
||||
// isn't a walkable path on the filesystem inside the sandbox.
|
||||
TEST_CHECK_SUCCESS(fstatat(proc_self_fd.get(), "0", &s, 0));
|
||||
};
|
||||
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
|
||||
}
|
||||
|
||||
// This test will verify that when you hold a fd to proc before entering
|
||||
@@ -209,28 +223,30 @@ TEST(ChrootTest, ProcMemSelfFdsNoEscapeProcOpen) {
|
||||
const FileDescriptor proc =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Open("/proc", O_RDONLY));
|
||||
|
||||
// Create and enter a chroot directory.
|
||||
const auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
|
||||
ASSERT_THAT(chroot(temp_dir.path().c_str()), SyscallSucceeds());
|
||||
const auto rest = [&] {
|
||||
// Create and enter a chroot directory.
|
||||
const auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
|
||||
TEST_CHECK_SUCCESS(chroot(temp_dir.path().c_str()));
|
||||
|
||||
// Open a file inside the chroot at /foo.
|
||||
const FileDescriptor foo =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Open("/foo", O_CREAT | O_RDONLY, 0644));
|
||||
// Open a file inside the chroot at /foo.
|
||||
const FileDescriptor foo =
|
||||
TEST_CHECK_NO_ERRNO_AND_VALUE(Open("/foo", O_CREAT | O_RDONLY, 0644));
|
||||
|
||||
// Examine /proc/self/fd/{foo_fd} to see if it exposes the fact that we're
|
||||
// inside a chroot, the path should be /foo and NOT {chroot_dir}/foo.
|
||||
const std::string fd_path = absl::StrCat("self/fd/", foo.get());
|
||||
char buf[1024] = {};
|
||||
size_t bytes_read = 0;
|
||||
ASSERT_THAT(bytes_read =
|
||||
readlinkat(proc.get(), fd_path.c_str(), buf, sizeof(buf) - 1),
|
||||
SyscallSucceeds());
|
||||
// Examine /proc/self/fd/{foo_fd} to see if it exposes the fact that we're
|
||||
// inside a chroot, the path should be /foo and NOT {chroot_dir}/foo.
|
||||
const std::string fd_path = absl::StrCat("self/fd/", foo.get());
|
||||
char buf[1024] = {};
|
||||
size_t bytes_read = 0;
|
||||
TEST_CHECK_SUCCESS(bytes_read = readlinkat(proc.get(), fd_path.c_str(), buf,
|
||||
sizeof(buf) - 1));
|
||||
|
||||
// The link should resolve to something.
|
||||
ASSERT_GT(bytes_read, 0);
|
||||
// The link should resolve to something.
|
||||
TEST_CHECK(bytes_read > 0);
|
||||
|
||||
// Assert that the link doesn't contain the chroot path and is only /foo.
|
||||
EXPECT_STREQ(buf, "/foo");
|
||||
// Assert that the link doesn't contain the chroot path and is only /foo.
|
||||
TEST_CHECK(strcmp(buf, "/foo") == 0);
|
||||
};
|
||||
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
|
||||
}
|
||||
|
||||
// This test will verify that a file inside a chroot when mmapped will not
|
||||
@@ -242,39 +258,41 @@ TEST(ChrootTest, ProcMemSelfMapsNoEscapeProcOpen) {
|
||||
const FileDescriptor proc =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Open("/proc", O_RDONLY));
|
||||
|
||||
// Create and enter a chroot directory.
|
||||
const auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
|
||||
ASSERT_THAT(chroot(temp_dir.path().c_str()), SyscallSucceeds());
|
||||
const auto rest = [&] {
|
||||
// Create and enter a chroot directory.
|
||||
const auto temp_dir = TEST_CHECK_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
|
||||
TEST_CHECK_SUCCESS(chroot(temp_dir.path().c_str()));
|
||||
|
||||
// Open a file inside the chroot at /foo.
|
||||
const FileDescriptor foo =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Open("/foo", O_CREAT | O_RDONLY, 0644));
|
||||
// Open a file inside the chroot at /foo.
|
||||
const FileDescriptor foo =
|
||||
TEST_CHECK_NO_ERRNO_AND_VALUE(Open("/foo", O_CREAT | O_RDONLY, 0644));
|
||||
|
||||
// Mmap the newly created file.
|
||||
void* foo_map = mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE,
|
||||
foo.get(), 0);
|
||||
ASSERT_THAT(reinterpret_cast<int64_t>(foo_map), SyscallSucceeds());
|
||||
// Mmap the newly created file.
|
||||
void* foo_map = mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE, foo.get(), 0);
|
||||
TEST_CHECK_SUCCESS(reinterpret_cast<int64_t>(foo_map));
|
||||
|
||||
// Always unmap.
|
||||
auto cleanup_map = Cleanup(
|
||||
[&] { EXPECT_THAT(munmap(foo_map, kPageSize), SyscallSucceeds()); });
|
||||
// Always unmap.
|
||||
auto cleanup_map =
|
||||
Cleanup([&] { TEST_CHECK_SUCCESS(munmap(foo_map, kPageSize)); });
|
||||
|
||||
// Examine /proc/self/maps to be sure that /foo doesn't appear to be
|
||||
// mapped with the full chroot path.
|
||||
const FileDescriptor maps =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(OpenAt(proc.get(), "self/maps", O_RDONLY));
|
||||
// Examine /proc/self/maps to be sure that /foo doesn't appear to be
|
||||
// mapped with the full chroot path.
|
||||
const FileDescriptor maps = TEST_CHECK_NO_ERRNO_AND_VALUE(
|
||||
OpenAt(proc.get(), "self/maps", O_RDONLY));
|
||||
|
||||
size_t bytes_read = 0;
|
||||
char buf[8 * 1024] = {};
|
||||
ASSERT_THAT(bytes_read = ReadFd(maps.get(), buf, sizeof(buf)),
|
||||
SyscallSucceeds());
|
||||
size_t bytes_read = 0;
|
||||
char buf[8 * 1024] = {};
|
||||
TEST_CHECK_SUCCESS(bytes_read = ReadFd(maps.get(), buf, sizeof(buf)));
|
||||
|
||||
// The maps file should have something.
|
||||
ASSERT_GT(bytes_read, 0);
|
||||
// The maps file should have something.
|
||||
TEST_CHECK(bytes_read > 0);
|
||||
|
||||
// Finally we want to make sure the maps don't contain the chroot path
|
||||
ASSERT_EQ(std::string(buf, bytes_read).find(temp_dir.path()),
|
||||
std::string::npos);
|
||||
// Finally we want to make sure the maps don't contain the chroot path
|
||||
TEST_CHECK(std::string(buf, bytes_read).find(temp_dir.path()) ==
|
||||
std::string::npos);
|
||||
};
|
||||
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
|
||||
}
|
||||
|
||||
// Test that mounts outside the chroot will not appear in /proc/self/mounts or
|
||||
@@ -283,81 +301,76 @@ TEST(ChrootTest, ProcMountsMountinfoNoEscape) {
|
||||
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
|
||||
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT)));
|
||||
|
||||
// We are going to create some mounts and then chroot. In order to be able to
|
||||
// unmount the mounts after the test run, we must chdir to the root and use
|
||||
// relative paths for all mounts. That way, as long as we never chdir into
|
||||
// the new root, we can access the mounts via relative paths and unmount them.
|
||||
ASSERT_THAT(chdir("/"), SyscallSucceeds());
|
||||
|
||||
// Create nested tmpfs mounts. Note the use of relative paths in Mount calls.
|
||||
// Create nested tmpfs mounts.
|
||||
auto const outer_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
|
||||
auto const outer_mount = ASSERT_NO_ERRNO_AND_VALUE(Mount(
|
||||
"none", JoinPath(".", outer_dir.path()), "tmpfs", 0, "mode=0700", 0));
|
||||
auto const outer_mount = ASSERT_NO_ERRNO_AND_VALUE(
|
||||
Mount("none", outer_dir.path(), "tmpfs", 0, "mode=0700", 0));
|
||||
|
||||
auto const inner_dir =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(outer_dir.path()));
|
||||
auto const inner_mount = ASSERT_NO_ERRNO_AND_VALUE(Mount(
|
||||
"none", JoinPath(".", inner_dir.path()), "tmpfs", 0, "mode=0700", 0));
|
||||
auto const inner_mount = ASSERT_NO_ERRNO_AND_VALUE(
|
||||
Mount("none", inner_dir.path(), "tmpfs", 0, "mode=0700", 0));
|
||||
|
||||
// Filenames that will be checked for mounts, all relative to /proc dir.
|
||||
std::string paths[3] = {"mounts", "self/mounts", "self/mountinfo"};
|
||||
const auto rest = [&outer_dir, &inner_dir] {
|
||||
// Filenames that will be checked for mounts, all relative to /proc dir.
|
||||
std::string paths[3] = {"mounts", "self/mounts", "self/mountinfo"};
|
||||
|
||||
for (const std::string& path : paths) {
|
||||
// We should have both inner and outer mounts.
|
||||
const std::string contents =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(GetContents(JoinPath("/proc", path)));
|
||||
EXPECT_THAT(contents, AllOf(HasSubstr(outer_dir.path()),
|
||||
HasSubstr(inner_dir.path())));
|
||||
// We better have at least two mounts: the mounts we created plus the root.
|
||||
std::vector<absl::string_view> submounts =
|
||||
absl::StrSplit(contents, '\n', absl::SkipWhitespace());
|
||||
EXPECT_GT(submounts.size(), 2);
|
||||
}
|
||||
for (const std::string& path : paths) {
|
||||
// We should have both inner and outer mounts.
|
||||
const std::string contents =
|
||||
TEST_CHECK_NO_ERRNO_AND_VALUE(GetContents(JoinPath("/proc", path)));
|
||||
EXPECT_THAT(contents, AllOf(HasSubstr(outer_dir.path()),
|
||||
HasSubstr(inner_dir.path())));
|
||||
// We better have at least two mounts: the mounts we created plus the
|
||||
// root.
|
||||
std::vector<absl::string_view> submounts =
|
||||
absl::StrSplit(contents, '\n', absl::SkipWhitespace());
|
||||
TEST_CHECK(submounts.size() > 2);
|
||||
}
|
||||
|
||||
// Get a FD to /proc before we enter the chroot.
|
||||
const FileDescriptor proc =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Open("/proc", O_RDONLY));
|
||||
// Get a FD to /proc before we enter the chroot.
|
||||
const FileDescriptor proc =
|
||||
TEST_CHECK_NO_ERRNO_AND_VALUE(Open("/proc", O_RDONLY));
|
||||
|
||||
// Chroot to outer mount.
|
||||
ASSERT_THAT(chroot(outer_dir.path().c_str()), SyscallSucceeds());
|
||||
// Chroot to outer mount.
|
||||
TEST_CHECK_SUCCESS(chroot(outer_dir.path().c_str()));
|
||||
|
||||
for (const std::string& path : paths) {
|
||||
const FileDescriptor proc_file =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(OpenAt(proc.get(), path, O_RDONLY));
|
||||
for (const std::string& path : paths) {
|
||||
const FileDescriptor proc_file =
|
||||
TEST_CHECK_NO_ERRNO_AND_VALUE(OpenAt(proc.get(), path, O_RDONLY));
|
||||
|
||||
// Only two mounts visible from this chroot: the inner and outer. Both
|
||||
// paths should be relative to the new chroot.
|
||||
const std::string contents =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(GetContentsFD(proc_file.get()));
|
||||
EXPECT_THAT(contents,
|
||||
AllOf(HasSubstr(absl::StrCat(Basename(inner_dir.path()))),
|
||||
Not(HasSubstr(outer_dir.path())),
|
||||
Not(HasSubstr(inner_dir.path()))));
|
||||
std::vector<absl::string_view> submounts =
|
||||
absl::StrSplit(contents, '\n', absl::SkipWhitespace());
|
||||
EXPECT_EQ(submounts.size(), 2);
|
||||
}
|
||||
// Only two mounts visible from this chroot: the inner and outer. Both
|
||||
// paths should be relative to the new chroot.
|
||||
const std::string contents =
|
||||
TEST_CHECK_NO_ERRNO_AND_VALUE(GetContentsFD(proc_file.get()));
|
||||
EXPECT_THAT(contents,
|
||||
AllOf(HasSubstr(absl::StrCat(Basename(inner_dir.path()))),
|
||||
Not(HasSubstr(outer_dir.path())),
|
||||
Not(HasSubstr(inner_dir.path()))));
|
||||
std::vector<absl::string_view> submounts =
|
||||
absl::StrSplit(contents, '\n', absl::SkipWhitespace());
|
||||
TEST_CHECK(submounts.size() == 2);
|
||||
}
|
||||
|
||||
// Chroot to inner mount. We must use an absolute path accessible to our
|
||||
// chroot.
|
||||
const std::string inner_dir_basename =
|
||||
absl::StrCat("/", Basename(inner_dir.path()));
|
||||
ASSERT_THAT(chroot(inner_dir_basename.c_str()), SyscallSucceeds());
|
||||
// Chroot to inner mount. We must use an absolute path accessible to our
|
||||
// chroot.
|
||||
const std::string inner_dir_basename =
|
||||
absl::StrCat("/", Basename(inner_dir.path()));
|
||||
TEST_CHECK_SUCCESS(chroot(inner_dir_basename.c_str()));
|
||||
|
||||
for (const std::string& path : paths) {
|
||||
const FileDescriptor proc_file =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(OpenAt(proc.get(), path, O_RDONLY));
|
||||
const std::string contents =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(GetContentsFD(proc_file.get()));
|
||||
for (const std::string& path : paths) {
|
||||
const FileDescriptor proc_file =
|
||||
TEST_CHECK_NO_ERRNO_AND_VALUE(OpenAt(proc.get(), path, O_RDONLY));
|
||||
const std::string contents =
|
||||
TEST_CHECK_NO_ERRNO_AND_VALUE(GetContentsFD(proc_file.get()));
|
||||
|
||||
// Only the inner mount visible from this chroot.
|
||||
std::vector<absl::string_view> submounts =
|
||||
absl::StrSplit(contents, '\n', absl::SkipWhitespace());
|
||||
EXPECT_EQ(submounts.size(), 1);
|
||||
}
|
||||
|
||||
// Chroot back to ".".
|
||||
ASSERT_THAT(chroot("."), SyscallSucceeds());
|
||||
// Only the inner mount visible from this chroot.
|
||||
std::vector<absl::string_view> submounts =
|
||||
absl::StrSplit(contents, '\n', absl::SkipWhitespace());
|
||||
TEST_CHECK(submounts.size() == 1);
|
||||
}
|
||||
};
|
||||
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "absl/time/time.h"
|
||||
#include "test/util/logging.h"
|
||||
#include "test/util/memory_util.h"
|
||||
#include "test/util/multiprocess_util.h"
|
||||
#include "test/util/signal_util.h"
|
||||
#include "test/util/test_util.h"
|
||||
|
||||
@@ -93,59 +94,66 @@ TEST(GetrusageTest, Grandchild) {
|
||||
// Verifies that processes ignoring SIGCHLD do not have updated child maxrss
|
||||
// updated.
|
||||
TEST(GetrusageTest, IgnoreSIGCHLD) {
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sa.sa_flags = 0;
|
||||
auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGCHLD, sa));
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
const auto rest = [] {
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sa.sa_flags = 0;
|
||||
auto cleanup = TEST_CHECK_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGCHLD, sa));
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
struct rusage rusage_self;
|
||||
TEST_PCHECK(getrusage(RUSAGE_SELF, &rusage_self) == 0);
|
||||
// The child has consumed some memory.
|
||||
TEST_CHECK(rusage_self.ru_maxrss != 0);
|
||||
_exit(0);
|
||||
}
|
||||
TEST_CHECK_SUCCESS(pid);
|
||||
int status;
|
||||
TEST_CHECK_ERRNO(RetryEINTR(waitpid)(pid, &status, 0), ECHILD);
|
||||
struct rusage rusage_self;
|
||||
TEST_PCHECK(getrusage(RUSAGE_SELF, &rusage_self) == 0);
|
||||
// The child has consumed some memory.
|
||||
TEST_CHECK(rusage_self.ru_maxrss != 0);
|
||||
_exit(0);
|
||||
}
|
||||
ASSERT_THAT(pid, SyscallSucceeds());
|
||||
int status;
|
||||
ASSERT_THAT(RetryEINTR(waitpid)(pid, &status, 0),
|
||||
SyscallFailsWithErrno(ECHILD));
|
||||
struct rusage rusage_self;
|
||||
ASSERT_THAT(getrusage(RUSAGE_SELF, &rusage_self), SyscallSucceeds());
|
||||
struct rusage rusage_children;
|
||||
ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &rusage_children), SyscallSucceeds());
|
||||
// The parent has consumed some memory.
|
||||
EXPECT_GT(rusage_self.ru_maxrss, 0);
|
||||
// The child's maxrss should not have propagated up.
|
||||
EXPECT_EQ(rusage_children.ru_maxrss, 0);
|
||||
TEST_CHECK_SUCCESS(getrusage(RUSAGE_SELF, &rusage_self));
|
||||
struct rusage rusage_children;
|
||||
TEST_CHECK_SUCCESS(getrusage(RUSAGE_CHILDREN, &rusage_children));
|
||||
// The parent has consumed some memory.
|
||||
TEST_CHECK(rusage_self.ru_maxrss > 0);
|
||||
// The child's maxrss should not have propagated up.
|
||||
TEST_CHECK(rusage_children.ru_maxrss == 0);
|
||||
};
|
||||
// Execute inside a forked process so that rusage_children is clean.
|
||||
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
|
||||
}
|
||||
|
||||
// Verifies that zombie processes do not update their parent's maxrss. Only
|
||||
// reaped processes should do this.
|
||||
TEST(GetrusageTest, IgnoreZombie) {
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
const auto rest = [] {
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
struct rusage rusage_self;
|
||||
TEST_PCHECK(getrusage(RUSAGE_SELF, &rusage_self) == 0);
|
||||
struct rusage rusage_children;
|
||||
TEST_PCHECK(getrusage(RUSAGE_CHILDREN, &rusage_children) == 0);
|
||||
// The child has consumed some memory.
|
||||
TEST_CHECK(rusage_self.ru_maxrss != 0);
|
||||
// The child has no children of its own.
|
||||
TEST_CHECK(rusage_children.ru_maxrss == 0);
|
||||
_exit(0);
|
||||
}
|
||||
TEST_CHECK_SUCCESS(pid);
|
||||
// Give the child time to exit. Because we don't call wait, the child should
|
||||
// remain a zombie.
|
||||
absl::SleepFor(absl::Seconds(5));
|
||||
struct rusage rusage_self;
|
||||
TEST_PCHECK(getrusage(RUSAGE_SELF, &rusage_self) == 0);
|
||||
TEST_CHECK_SUCCESS(getrusage(RUSAGE_SELF, &rusage_self));
|
||||
struct rusage rusage_children;
|
||||
TEST_PCHECK(getrusage(RUSAGE_CHILDREN, &rusage_children) == 0);
|
||||
// The child has consumed some memory.
|
||||
TEST_CHECK(rusage_self.ru_maxrss != 0);
|
||||
// The child has no children of its own.
|
||||
TEST_CHECK_SUCCESS(getrusage(RUSAGE_CHILDREN, &rusage_children));
|
||||
// The parent has consumed some memory.
|
||||
TEST_CHECK(rusage_self.ru_maxrss > 0);
|
||||
// The child has consumed some memory, but hasn't been reaped.
|
||||
TEST_CHECK(rusage_children.ru_maxrss == 0);
|
||||
_exit(0);
|
||||
}
|
||||
ASSERT_THAT(pid, SyscallSucceeds());
|
||||
// Give the child time to exit. Because we don't call wait, the child should
|
||||
// remain a zombie.
|
||||
absl::SleepFor(absl::Seconds(5));
|
||||
struct rusage rusage_self;
|
||||
ASSERT_THAT(getrusage(RUSAGE_SELF, &rusage_self), SyscallSucceeds());
|
||||
struct rusage rusage_children;
|
||||
ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &rusage_children), SyscallSucceeds());
|
||||
// The parent has consumed some memory.
|
||||
EXPECT_GT(rusage_self.ru_maxrss, 0);
|
||||
// The child has consumed some memory, but hasn't been reaped.
|
||||
EXPECT_EQ(rusage_children.ru_maxrss, 0);
|
||||
};
|
||||
// Execute inside a forked process so that rusage_children is clean.
|
||||
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
|
||||
}
|
||||
|
||||
TEST(GetrusageTest, Wait4) {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "absl/strings/str_join.h"
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
|
||||
#include "test/util/cleanup.h"
|
||||
#include "test/util/file_descriptor.h"
|
||||
#include "test/util/fs_util.h"
|
||||
#include "test/util/test_util.h"
|
||||
@@ -341,6 +342,8 @@ TEST(ProcNetUnix, StreamSocketStateStateConnectedOnAccept) {
|
||||
int clientfd;
|
||||
ASSERT_THAT(clientfd = accept(sockets->first_fd(), nullptr, nullptr),
|
||||
SyscallSucceeds());
|
||||
auto cleanup = Cleanup(
|
||||
[clientfd]() { ASSERT_THAT(close(clientfd), SyscallSucceeds()); });
|
||||
|
||||
// Find the entry for the accepted socket. UDS proc entries don't have a
|
||||
// remote address, so we distinguish the accepted socket from the listen
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_join.h"
|
||||
#include "test/util/capability_util.h"
|
||||
#include "test/util/cleanup.h"
|
||||
#include "test/util/multiprocess_util.h"
|
||||
#include "test/util/posix_error.h"
|
||||
#include "test/util/test_util.h"
|
||||
#include "test/util/thread_util.h"
|
||||
@@ -33,6 +35,16 @@ ABSL_FLAG(int32_t, scratch_uid2, 65533, "second scratch UID");
|
||||
ABSL_FLAG(int32_t, scratch_gid1, 65534, "first scratch GID");
|
||||
ABSL_FLAG(int32_t, scratch_gid2, 65533, "second scratch GID");
|
||||
|
||||
// Force use of syscall instead of glibc set*id() wrappers because we want to
|
||||
// apply to the current task only. libc sets all threads in a process because
|
||||
// "POSIX requires that all threads in a process share the same credentials."
|
||||
#define setuid USE_SYSCALL_INSTEAD
|
||||
#define setgid USE_SYSCALL_INSTEAD
|
||||
#define setreuid USE_SYSCALL_INSTEAD
|
||||
#define setregid USE_SYSCALL_INSTEAD
|
||||
#define setresuid USE_SYSCALL_INSTEAD
|
||||
#define setresgid USE_SYSCALL_INSTEAD
|
||||
|
||||
using ::testing::UnorderedElementsAreArray;
|
||||
|
||||
namespace gvisor {
|
||||
@@ -137,21 +149,31 @@ TEST(UidGidRootTest, Setuid) {
|
||||
TEST(UidGidRootTest, Setgid) {
|
||||
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot()));
|
||||
|
||||
EXPECT_THAT(setgid(-1), SyscallFailsWithErrno(EINVAL));
|
||||
EXPECT_THAT(syscall(SYS_setgid, -1), SyscallFailsWithErrno(EINVAL));
|
||||
|
||||
const gid_t gid = absl::GetFlag(FLAGS_scratch_gid1);
|
||||
ASSERT_THAT(setgid(gid), SyscallSucceeds());
|
||||
EXPECT_NO_ERRNO(CheckGIDs(gid, gid, gid));
|
||||
ScopedThread([&] {
|
||||
const gid_t gid = absl::GetFlag(FLAGS_scratch_gid1);
|
||||
EXPECT_THAT(syscall(SYS_setgid, gid), SyscallSucceeds());
|
||||
EXPECT_NO_ERRNO(CheckGIDs(gid, gid, gid));
|
||||
});
|
||||
}
|
||||
|
||||
TEST(UidGidRootTest, SetgidNotFromThreadGroupLeader) {
|
||||
#pragma push_macro("allow_setgid")
|
||||
#undef setgid
|
||||
|
||||
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot()));
|
||||
|
||||
int old_gid = getgid();
|
||||
auto clean = Cleanup([old_gid] { setgid(old_gid); });
|
||||
|
||||
const gid_t gid = absl::GetFlag(FLAGS_scratch_gid1);
|
||||
// NOTE(b/64676707): Do setgid in a separate thread so that we can test if
|
||||
// info.si_pid is set correctly.
|
||||
ScopedThread([gid] { ASSERT_THAT(setgid(gid), SyscallSucceeds()); });
|
||||
EXPECT_NO_ERRNO(CheckGIDs(gid, gid, gid));
|
||||
|
||||
#pragma pop_macro("allow_setgid")
|
||||
}
|
||||
|
||||
TEST(UidGidRootTest, Setreuid) {
|
||||
@@ -159,27 +181,25 @@ TEST(UidGidRootTest, Setreuid) {
|
||||
|
||||
// "Supplying a value of -1 for either the real or effective user ID forces
|
||||
// the system to leave that ID unchanged." - setreuid(2)
|
||||
EXPECT_THAT(setreuid(-1, -1), SyscallSucceeds());
|
||||
EXPECT_THAT(syscall(SYS_setreuid, -1, -1), SyscallSucceeds());
|
||||
|
||||
EXPECT_NO_ERRNO(CheckUIDs(0, 0, 0));
|
||||
|
||||
// Do setuid in a separate thread so that after finishing this test, the
|
||||
// process can still open files the test harness created before starting this
|
||||
// test. Otherwise, the files are created by root (UID before the test), but
|
||||
// cannot be opened by the `uid` set below after the test. After calling
|
||||
// setuid(non-zero-UID), there is no way to get root privileges back.
|
||||
// process can still open files the test harness created before starting
|
||||
// this test. Otherwise, the files are created by root (UID before the
|
||||
// test), but cannot be opened by the `uid` set below after the test. After
|
||||
// calling setuid(non-zero-UID), there is no way to get root privileges
|
||||
// back.
|
||||
ScopedThread([&] {
|
||||
const uid_t ruid = absl::GetFlag(FLAGS_scratch_uid1);
|
||||
const uid_t euid = absl::GetFlag(FLAGS_scratch_uid2);
|
||||
|
||||
// Use syscall instead of glibc setuid wrapper because we want this setuid
|
||||
// call to only apply to this task. posix threads, however, require that all
|
||||
// threads have the same UIDs, so using the setuid wrapper sets all threads'
|
||||
// real UID.
|
||||
EXPECT_THAT(syscall(SYS_setreuid, ruid, euid), SyscallSucceeds());
|
||||
|
||||
// "If the real user ID is set or the effective user ID is set to a value
|
||||
// not equal to the previous real user ID, the saved set-user-ID will be set
|
||||
// to the new effective user ID." - setreuid(2)
|
||||
// not equal to the previous real user ID, the saved set-user-ID will be
|
||||
// set to the new effective user ID." - setreuid(2)
|
||||
EXPECT_NO_ERRNO(CheckUIDs(ruid, euid, euid));
|
||||
});
|
||||
}
|
||||
@@ -187,13 +207,15 @@ TEST(UidGidRootTest, Setreuid) {
|
||||
TEST(UidGidRootTest, Setregid) {
|
||||
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot()));
|
||||
|
||||
EXPECT_THAT(setregid(-1, -1), SyscallSucceeds());
|
||||
EXPECT_THAT(syscall(SYS_setregid, -1, -1), SyscallSucceeds());
|
||||
EXPECT_NO_ERRNO(CheckGIDs(0, 0, 0));
|
||||
|
||||
const gid_t rgid = absl::GetFlag(FLAGS_scratch_gid1);
|
||||
const gid_t egid = absl::GetFlag(FLAGS_scratch_gid2);
|
||||
ASSERT_THAT(setregid(rgid, egid), SyscallSucceeds());
|
||||
EXPECT_NO_ERRNO(CheckGIDs(rgid, egid, egid));
|
||||
ScopedThread([&] {
|
||||
const gid_t rgid = absl::GetFlag(FLAGS_scratch_gid1);
|
||||
const gid_t egid = absl::GetFlag(FLAGS_scratch_gid2);
|
||||
ASSERT_THAT(syscall(SYS_setregid, rgid, egid), SyscallSucceeds());
|
||||
EXPECT_NO_ERRNO(CheckGIDs(rgid, egid, egid));
|
||||
});
|
||||
}
|
||||
|
||||
TEST(UidGidRootTest, Setresuid) {
|
||||
@@ -201,23 +223,24 @@ TEST(UidGidRootTest, Setresuid) {
|
||||
|
||||
// "If one of the arguments equals -1, the corresponding value is not
|
||||
// changed." - setresuid(2)
|
||||
EXPECT_THAT(setresuid(-1, -1, -1), SyscallSucceeds());
|
||||
EXPECT_THAT(syscall(SYS_setresuid, -1, -1, -1), SyscallSucceeds());
|
||||
EXPECT_NO_ERRNO(CheckUIDs(0, 0, 0));
|
||||
|
||||
// Do setuid in a separate thread so that after finishing this test, the
|
||||
// process can still open files the test harness created before starting this
|
||||
// test. Otherwise, the files are created by root (UID before the test), but
|
||||
// cannot be opened by the `uid` set below after the test. After calling
|
||||
// setuid(non-zero-UID), there is no way to get root privileges back.
|
||||
// process can still open files the test harness created before starting
|
||||
// this test. Otherwise, the files are created by root (UID before the
|
||||
// test), but cannot be opened by the `uid` set below after the test. After
|
||||
// calling setuid(non-zero-UID), there is no way to get root privileges
|
||||
// back.
|
||||
ScopedThread([&] {
|
||||
const uid_t ruid = 12345;
|
||||
const uid_t euid = 23456;
|
||||
const uid_t suid = 34567;
|
||||
|
||||
// Use syscall instead of glibc setuid wrapper because we want this setuid
|
||||
// call to only apply to this task. posix threads, however, require that all
|
||||
// threads have the same UIDs, so using the setuid wrapper sets all threads'
|
||||
// real UID.
|
||||
// call to only apply to this task. posix threads, however, require that
|
||||
// all threads have the same UIDs, so using the setuid wrapper sets all
|
||||
// threads' real UID.
|
||||
EXPECT_THAT(syscall(SYS_setresuid, ruid, euid, suid), SyscallSucceeds());
|
||||
EXPECT_NO_ERRNO(CheckUIDs(ruid, euid, suid));
|
||||
});
|
||||
@@ -226,14 +249,16 @@ TEST(UidGidRootTest, Setresuid) {
|
||||
TEST(UidGidRootTest, Setresgid) {
|
||||
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot()));
|
||||
|
||||
EXPECT_THAT(setresgid(-1, -1, -1), SyscallSucceeds());
|
||||
EXPECT_THAT(syscall(SYS_setresgid, -1, -1, -1), SyscallSucceeds());
|
||||
EXPECT_NO_ERRNO(CheckGIDs(0, 0, 0));
|
||||
|
||||
const gid_t rgid = 12345;
|
||||
const gid_t egid = 23456;
|
||||
const gid_t sgid = 34567;
|
||||
ASSERT_THAT(setresgid(rgid, egid, sgid), SyscallSucceeds());
|
||||
EXPECT_NO_ERRNO(CheckGIDs(rgid, egid, sgid));
|
||||
ScopedThread([&] {
|
||||
const gid_t rgid = 12345;
|
||||
const gid_t egid = 23456;
|
||||
const gid_t sgid = 34567;
|
||||
ASSERT_THAT(syscall(SYS_setresgid, rgid, egid, sgid), SyscallSucceeds());
|
||||
EXPECT_NO_ERRNO(CheckGIDs(rgid, egid, sgid));
|
||||
});
|
||||
}
|
||||
|
||||
TEST(UidGidRootTest, Setgroups) {
|
||||
@@ -254,14 +279,14 @@ TEST(UidGidRootTest, Setuid_prlimit) {
|
||||
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot()));
|
||||
|
||||
// Do seteuid in a separate thread so that after finishing this test, the
|
||||
// process can still open files the test harness created before starting this
|
||||
// test. Otherwise, the files are created by root (UID before the test), but
|
||||
// cannot be opened by the `uid` set below after the test.
|
||||
// process can still open files the test harness created before starting
|
||||
// this test. Otherwise, the files are created by root (UID before the
|
||||
// test), but cannot be opened by the `uid` set below after the test.
|
||||
ScopedThread([&] {
|
||||
// Use syscall instead of glibc setuid wrapper because we want this seteuid
|
||||
// call to only apply to this task. POSIX threads, however, require that all
|
||||
// threads have the same UIDs, so using the seteuid wrapper sets all
|
||||
// threads' UID.
|
||||
// Use syscall instead of glibc setuid wrapper because we want this
|
||||
// seteuid call to only apply to this task. POSIX threads, however,
|
||||
// require that all threads have the same UIDs, so using the seteuid
|
||||
// wrapper sets all threads' UID.
|
||||
EXPECT_THAT(syscall(SYS_setreuid, -1, 65534), SyscallSucceeds());
|
||||
|
||||
// Despite the UID change, we should be able to get our own limits.
|
||||
|
||||
@@ -96,6 +96,19 @@ inline PosixError DropPermittedCapability(int cap) {
|
||||
|
||||
PosixErrorOr<bool> CanCreateUserNamespace();
|
||||
|
||||
class AutoCapability {
|
||||
public:
|
||||
AutoCapability(int cap, bool set) : cap_(cap), set_(set) {
|
||||
EXPECT_NO_ERRNO(SetCapability(cap_, set_));
|
||||
}
|
||||
|
||||
~AutoCapability() { EXPECT_NO_ERRNO(SetCapability(cap_, !set_)); }
|
||||
|
||||
private:
|
||||
int cap_;
|
||||
bool set_;
|
||||
};
|
||||
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
#endif // GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_
|
||||
|
||||
@@ -96,6 +96,21 @@ void CheckFailure(const char* cond, size_t cond_size, const char* msg,
|
||||
std::move(_expr_result).ValueOrDie(); \
|
||||
})
|
||||
|
||||
// cond must be greater or equal than 0. Used to test result of syscalls.
|
||||
//
|
||||
// This macro is async-signal-safe.
|
||||
#define TEST_CHECK_SUCCESS(cond) TEST_PCHECK((cond) >= 0)
|
||||
|
||||
// cond must be -1 and errno must match errno_value. Used to test errors from
|
||||
// syscalls.
|
||||
//
|
||||
// This macro is async-signal-safe.
|
||||
#define TEST_CHECK_ERRNO(cond, errno_value) \
|
||||
do { \
|
||||
TEST_PCHECK((cond) == -1); \
|
||||
TEST_PCHECK_MSG(errno == (errno_value), #cond " expected " #errno_value); \
|
||||
} while (0)
|
||||
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
|
||||
|
||||
Reference in New Issue
Block a user