diff --git a/sandboxed_api/sandbox2/BUILD.bazel b/sandboxed_api/sandbox2/BUILD.bazel index 212841c..959244b 100644 --- a/sandboxed_api/sandbox2/BUILD.bazel +++ b/sandboxed_api/sandbox2/BUILD.bazel @@ -774,7 +774,10 @@ cc_test( name = "sanitizer_test", srcs = ["sanitizer_test.cc"], copts = sapi_platform_copts(), - data = ["//sandboxed_api/sandbox2/testcases:sanitizer"], + data = [ + "//sandboxed_api/sandbox2/testcases:close_fds", + "//sandboxed_api/sandbox2/testcases:sanitizer", + ], tags = ["no_qemu_user_mode"], deps = [ ":comms", diff --git a/sandboxed_api/sandbox2/CMakeLists.txt b/sandboxed_api/sandbox2/CMakeLists.txt index f890350..276d2a6 100644 --- a/sandboxed_api/sandbox2/CMakeLists.txt +++ b/sandboxed_api/sandbox2/CMakeLists.txt @@ -874,6 +874,7 @@ if(SAPI_ENABLE_TESTS) ) add_dependencies(sandbox2_sanitizer_test sandbox2::testcase_sanitizer + sandbox2::testcase_close_fds ) target_link_libraries(sandbox2_sanitizer_test PRIVATE absl::memory diff --git a/sandboxed_api/sandbox2/sanitizer_test.cc b/sandboxed_api/sandbox2/sanitizer_test.cc index 870b9bd..e83b548 100644 --- a/sandboxed_api/sandbox2/sanitizer_test.cc +++ b/sandboxed_api/sandbox2/sanitizer_test.cc @@ -15,7 +15,6 @@ #include "sandboxed_api/sandbox2/sanitizer.h" #include -#include #include #include #include @@ -45,8 +44,6 @@ using ::sapi::GetTestSourcePath; using ::testing::Eq; using ::testing::Gt; -using ::testing::IsFalse; -using ::testing::IsTrue; using ::testing::Ne; namespace sandbox2 { @@ -59,12 +56,13 @@ int RunTestcase(const std::string& path, const std::vector& args) { PLOG(ERROR) << "fork()"; return 1; } + const char** argv = util::VecStringToCharPtrArr(args); if (pid == 0) { - const char** argv = util::VecStringToCharPtrArr(args); execv(path.c_str(), const_cast(argv)); PLOG(ERROR) << "execv('" << path << "')"; exit(EXIT_FAILURE); } + delete[] argv; for (;;) { int status; @@ -135,6 +133,26 @@ TEST(SanitizerTest, TestSandboxedBinary) { EXPECT_THAT(result.reason_code(), Eq(0)); } +// Test that sanitizer::CloseAllFDsExcept() closes all file descriptors except +// the ones listed. +TEST(SanitizerTest, TestCloseFDs) { + // Open a few file descriptors in non-close-on-exec mode. + int sock_fd[2]; + ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fd), Ne(-1)); + ASSERT_THAT(open("/dev/full", O_RDONLY), Ne(-1)); + int null_fd = open("/dev/null", O_RDWR); + ASSERT_THAT(null_fd, Ne(-1)); + + const std::string path = GetTestSourcePath("sandbox2/testcases/close_fds"); + std::vector args; + std::vector exceptions = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, + null_fd}; + for (auto fd : exceptions) { + args.push_back(absl::StrCat(fd)); + } + EXPECT_THAT(RunTestcase(path, args), Eq(0)); +} + TEST(SanitizerTest, TestGetProcStatusLine) { // Test indirectly, GetNumberOfThreads() looks for the "Threads" value. EXPECT_THAT(sanitizer::GetNumberOfThreads(getpid()), Gt(0)); diff --git a/sandboxed_api/sandbox2/testcases/BUILD.bazel b/sandboxed_api/sandbox2/testcases/BUILD.bazel index 4581442..2f9957e 100644 --- a/sandboxed_api/sandbox2/testcases/BUILD.bazel +++ b/sandboxed_api/sandbox2/testcases/BUILD.bazel @@ -164,6 +164,19 @@ cc_binary( linkstatic = 1, ) +cc_binary( + name = "close_fds", + testonly = 1, + srcs = ["close_fds.cc"], + copts = sapi_platform_copts(), + deps = [ + "//sandboxed_api/sandbox2:sanitizer", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/strings", + "@com_google_glog//:glog", + ], +) + # security: disable=cc-static-no-pie cc_binary( name = "sleep", diff --git a/sandboxed_api/sandbox2/testcases/CMakeLists.txt b/sandboxed_api/sandbox2/testcases/CMakeLists.txt index f954faa..0ec9d75 100644 --- a/sandboxed_api/sandbox2/testcases/CMakeLists.txt +++ b/sandboxed_api/sandbox2/testcases/CMakeLists.txt @@ -185,6 +185,24 @@ target_link_libraries(sandbox2_testcase_sanitizer PRIVATE ${_sandbox2_linkopts} ) +# sandboxed_api/sandbox2/testcases:close_fds +add_executable(sandbox2_testcase_close_fds + close_fds.cc +) +add_executable(sandbox2::testcase_close_fds ALIAS sandbox2_testcase_close_fds) +set_target_properties(sandbox2_testcase_close_fds PROPERTIES + OUTPUT_NAME close_fds +) +target_link_libraries(sandbox2_testcase_close_fds PRIVATE + sapi::base + ${_sandbox2_linkopts} + absl::strings + absl::flat_hash_set + glog::glog + sandbox2::sanitizer +) + + # sandboxed_api/sandbox2/testcases:sleep add_executable(sandbox2_testcase_sleep sleep.cc diff --git a/sandboxed_api/sandbox2/testcases/close_fds.cc b/sandboxed_api/sandbox2/testcases/close_fds.cc new file mode 100644 index 0000000..b106564 --- /dev/null +++ b/sandboxed_api/sandbox2/testcases/close_fds.cc @@ -0,0 +1,32 @@ +#include +#include +#include + +#include + +#include +#include "absl/container/flat_hash_set.h" +#include "absl/strings/numbers.h" +#include "sandboxed_api/sandbox2/sanitizer.h" + +bool IsFdOpen(int fd) { + int ret = fcntl(fd, F_GETFD); + if (ret == -1) { + CHECK(errno == EBADF); + return false; + } + return true; +} + +int main(int argc, char* argv[]) { + absl::flat_hash_set exceptions; + for (int i = 0; i < argc; ++i) { + int fd; + CHECK(absl::SimpleAtoi(argv[i], &fd)); + exceptions.insert(fd); + } + CHECK(sandbox2::sanitizer::CloseAllFDsExcept(exceptions).ok()); + for (int i = 0; i < INR_OPEN_MAX; i++) { + CHECK_EQ(IsFdOpen(i), exceptions.find(i) != exceptions.end()); + } +}