diff --git a/sandboxed_api/sandbox2/BUILD.bazel b/sandboxed_api/sandbox2/BUILD.bazel index cc8a253..82e74f1 100644 --- a/sandboxed_api/sandbox2/BUILD.bazel +++ b/sandboxed_api/sandbox2/BUILD.bazel @@ -975,6 +975,9 @@ cc_test( copts = sapi_platform_copts(), deps = [ ":util", + "//sandboxed_api/util:status_matchers", + "@com_google_absl//absl/cleanup", + "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ], ) diff --git a/sandboxed_api/sandbox2/CMakeLists.txt b/sandboxed_api/sandbox2/CMakeLists.txt index cd07d0a..ea0f887 100644 --- a/sandboxed_api/sandbox2/CMakeLists.txt +++ b/sandboxed_api/sandbox2/CMakeLists.txt @@ -1071,6 +1071,9 @@ if(BUILD_TESTING AND SAPI_BUILD_TESTING) ) target_link_libraries(sandbox2_util_test PRIVATE sandbox2::util + absl::strings + absl::cleanup + sapi::status_matchers sapi::test_main ) gtest_discover_tests_xcompile(sandbox2_util_test) diff --git a/sandboxed_api/sandbox2/util_test.cc b/sandboxed_api/sandbox2/util_test.cc index 0d8b20d..2a371e8 100644 --- a/sandboxed_api/sandbox2/util_test.cc +++ b/sandboxed_api/sandbox2/util_test.cc @@ -14,16 +14,32 @@ #include "sandboxed_api/sandbox2/util.h" +#include #include +#include +#include + #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/cleanup/cleanup.h" +#include "absl/strings/string_view.h" +#include "sandboxed_api/util/status_matchers.h" namespace sandbox2::util { namespace { +using ::sapi::IsOk; +using ::testing::ElementsAre; +using ::testing::Eq; using ::testing::Gt; +using ::testing::IsEmpty; using ::testing::IsTrue; +using ::testing::Ne; +using ::testing::Not; +using ::testing::StrEq; + +constexpr absl::string_view kTestString = "This is a test string"; TEST(UtilTest, TestCreateMemFd) { int fd = 0; @@ -32,5 +48,116 @@ TEST(UtilTest, TestCreateMemFd) { close(fd); } +TEST(CharPtrArrayTest, FromStringVector) { + std::vector strings = {"a", "b", "c"}; + CharPtrArray array = CharPtrArray::FromStringVector(strings); + EXPECT_THAT(array.ToStringVector(), Eq(strings)); + EXPECT_THAT(array.array(), + ElementsAre(StrEq("a"), StrEq("b"), StrEq("c"), nullptr)); + EXPECT_THAT(array.data(), Eq(array.array().data())); +} + +TEST(CharPtrArrayTest, FromCharPtrArray) { + std::vector strings = {"a", "b", "c"}; + std::vector string_arr; + for (std::string& s : strings) { + string_arr.push_back(s.data()); + } + string_arr.push_back(nullptr); + CharPtrArray array(string_arr.data()); + EXPECT_THAT(array.ToStringVector(), Eq(strings)); + EXPECT_THAT(array.array(), + ElementsAre(StrEq("a"), StrEq("b"), StrEq("c"), nullptr)); + EXPECT_THAT(array.data(), Eq(array.array().data())); +} + +TEST(GetProcStatusLineTest, Pid) { + std::string line = GetProcStatusLine(getpid(), "Pid"); + EXPECT_THAT(line, Eq(absl::StrCat(getpid()))); +} + +TEST(GetProcStatusLineTest, NonExisting) { + std::string line = + GetProcStatusLine(getpid(), "__N_o_n_ExistingStatusSetting"); + EXPECT_THAT(line, IsEmpty()); +} + +TEST(ForkWithFlagsTest, DoesForkNormally) { + int pfds[2]; + ASSERT_THAT(pipe(pfds), Eq(0)); + pid_t child = ForkWithFlags(SIGCHLD); + ASSERT_THAT(child, Ne(-1)); + if (child == 0) { + char c = 'a'; + if (!write(pfds[1], &c, 1)) { + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); + } + close(pfds[1]); + char c = ' '; + EXPECT_THAT(read(pfds[0], &c, 1), Eq(1)); + close(pfds[0]); + EXPECT_THAT(c, Eq('a')); + int status; + ASSERT_THAT(TEMP_FAILURE_RETRY(waitpid(child, &status, 0)), Eq(child)); + EXPECT_TRUE(WIFEXITED(status)); + EXPECT_THAT(WEXITSTATUS(status), Eq(0)); +} + +TEST(ForkWithFlagsTest, UnsupportedFlag) { + EXPECT_THAT(ForkWithFlags(CLONE_CHILD_CLEARTID), Eq(-1)); +} + +TEST(ReadCPathFromPidSplitPageTest, Normal) { + std::string test_str(kTestString); + absl::StatusOr read = + ReadCPathFromPid(getpid(), reinterpret_cast(test_str.data())); + ASSERT_THAT(read, IsOk()); + EXPECT_THAT(*read, Eq(kTestString)); +} + +TEST(ReadCPathFromPidSplitPageTest, Overlong) { + std::string test_str(PATH_MAX + 1, 'a'); + absl::StatusOr read = + ReadCPathFromPid(getpid(), reinterpret_cast(test_str.data())); + EXPECT_THAT(read, Not(IsOk())); +} + +TEST(ReadCPathFromPidSplitPageTest, SplitPage) { + const uintptr_t page_size = getpagesize(); + char* res = reinterpret_cast(mmap(nullptr, 2 * page_size, + PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, 0, 0)); + ASSERT_THAT(res, Ne(MAP_FAILED)); + absl::Cleanup cleanup = [res, page_size]() { + ASSERT_THAT(munmap(res, 2 * page_size), Eq(0)); + }; + char* str = &res[page_size - kTestString.size() / 2]; + memcpy(str, kTestString.data(), kTestString.size()); + absl::StatusOr read = + ReadCPathFromPid(getpid(), reinterpret_cast(str)); + ASSERT_THAT(read, IsOk()); + EXPECT_THAT(*read, Eq(kTestString)); +} + +TEST(ReadCPathFromPidSplitPageTest, NearUnreadableMemory) { + const uintptr_t page_size = getpagesize(); + char* res = reinterpret_cast(mmap(nullptr, 2 * page_size, + PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, 0, 0)); + ASSERT_THAT(res, Ne(MAP_FAILED)); + absl::Cleanup cleanup = [res, page_size]() { + ASSERT_THAT(munmap(res, 2 * page_size), Eq(0)); + }; + ASSERT_THAT(mprotect(&res[page_size], page_size, PROT_NONE), Eq(0)); + char* str = &res[page_size - kTestString.size() - 1]; + memcpy(str, kTestString.data(), kTestString.size()); + absl::StatusOr read = + ReadCPathFromPid(getpid(), reinterpret_cast(str)); + ASSERT_THAT(read, IsOk()); + EXPECT_THAT(*read, Eq(kTestString)); +} + } // namespace } // namespace sandbox2::util