mirror of
https://github.com/google/sandboxed-api.git
synced 2024-03-22 13:11:30 +08:00
Print final FS mounts in sandboxee's chroot
After all requested filesystem mounts are fully mounted under a sandboxee's virtual chroot, print a list of the outside paths and a list of the inside chroot paths that the outside paths are mapped to. This provides a valuable insight while debugging sandboxed binaries. PiperOrigin-RevId: 247130923 Change-Id: I42b4b3db68d826587c0fe8127aabbead38bc6f20
This commit is contained in:
parent
bfcd28bb91
commit
f29a5a81ed
|
@ -299,6 +299,7 @@ cc_library(
|
||||||
"//sandboxed_api/sandbox2/util:bpf_helper",
|
"//sandboxed_api/sandbox2/util:bpf_helper",
|
||||||
"//sandboxed_api/sandbox2/util:file_base",
|
"//sandboxed_api/sandbox2/util:file_base",
|
||||||
"//sandboxed_api/sandbox2/util:fileops",
|
"//sandboxed_api/sandbox2/util:fileops",
|
||||||
|
"//sandboxed_api/util:raw_logging",
|
||||||
"//sandboxed_api/util:status",
|
"//sandboxed_api/util:status",
|
||||||
"//sandboxed_api/util:statusor",
|
"//sandboxed_api/util:statusor",
|
||||||
|
|
||||||
|
@ -377,7 +378,6 @@ cc_library(
|
||||||
"@com_google_absl//absl/base:core_headers",
|
"@com_google_absl//absl/base:core_headers",
|
||||||
"@com_google_absl//absl/container:flat_hash_set",
|
"@com_google_absl//absl/container:flat_hash_set",
|
||||||
"@com_google_absl//absl/strings",
|
"@com_google_absl//absl/strings",
|
||||||
"@com_google_absl//absl/strings:str_format",
|
|
||||||
"@com_google_protobuf//:protobuf",
|
"@com_google_protobuf//:protobuf",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -390,6 +390,7 @@ cc_test(
|
||||||
":mounts",
|
":mounts",
|
||||||
":testing",
|
":testing",
|
||||||
"//sandboxed_api/sandbox2/util:file_base",
|
"//sandboxed_api/sandbox2/util:file_base",
|
||||||
|
"//sandboxed_api/sandbox2/util:file_helpers",
|
||||||
"//sandboxed_api/sandbox2/util:temp_file",
|
"//sandboxed_api/sandbox2/util:temp_file",
|
||||||
"//sandboxed_api/util:status_matchers",
|
"//sandboxed_api/util:status_matchers",
|
||||||
"@com_google_absl//absl/strings",
|
"@com_google_absl//absl/strings",
|
||||||
|
|
|
@ -16,9 +16,10 @@
|
||||||
|
|
||||||
#include "sandboxed_api/sandbox2/monitor.h"
|
#include "sandboxed_api/sandbox2/monitor.h"
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
#include <linux/posix_types.h> // NOLINT: Needs to come before linux/ipc.h
|
#include <linux/posix_types.h> // NOLINT: Needs to come before linux/ipc.h
|
||||||
|
|
||||||
#include <linux/ipc.h>
|
#include <linux/ipc.h>
|
||||||
|
// clang-format on
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/ptrace.h>
|
#include <sys/ptrace.h>
|
||||||
|
@ -59,6 +60,7 @@
|
||||||
#include "sandboxed_api/sandbox2/stack-trace.h"
|
#include "sandboxed_api/sandbox2/stack-trace.h"
|
||||||
#include "sandboxed_api/sandbox2/syscall.h"
|
#include "sandboxed_api/sandbox2/syscall.h"
|
||||||
#include "sandboxed_api/sandbox2/util.h"
|
#include "sandboxed_api/sandbox2/util.h"
|
||||||
|
#include "sandboxed_api/util/raw_logging.h"
|
||||||
|
|
||||||
ABSL_FLAG(bool, sandbox2_report_on_sandboxee_signal, true,
|
ABSL_FLAG(bool, sandbox2_report_on_sandboxee_signal, true,
|
||||||
"Report sandbox2 sandboxee deaths caused by signals");
|
"Report sandbox2 sandboxee deaths caused by signals");
|
||||||
|
@ -108,6 +110,16 @@ Monitor::~Monitor() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void LogContainer(const std::vector<std::string>& container) {
|
||||||
|
for (size_t i = 0; i < container.size(); ++i) {
|
||||||
|
SAPI_RAW_LOG(INFO, "[%4d]=%s", i, container[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void Monitor::Run() {
|
void Monitor::Run() {
|
||||||
using DecrementCounter = decltype(setup_counter_);
|
using DecrementCounter = decltype(setup_counter_);
|
||||||
std::unique_ptr<DecrementCounter, std::function<void(DecrementCounter*)>>
|
std::unique_ptr<DecrementCounter, std::function<void(DecrementCounter*)>>
|
||||||
|
@ -139,6 +151,18 @@ void Monitor::Run() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (SAPI_VLOG_IS_ON(1) && policy_->GetNamespace() != nullptr) {
|
||||||
|
std::vector<std::string> outside_entries;
|
||||||
|
std::vector<std::string> inside_entries;
|
||||||
|
policy_->GetNamespace()->mounts().RecursivelyListMounts(
|
||||||
|
/*outside_entries=*/&outside_entries,
|
||||||
|
/*inside_entries=*/&inside_entries);
|
||||||
|
SAPI_RAW_VLOG(1, "Outside entries mapped to chroot:");
|
||||||
|
LogContainer(outside_entries);
|
||||||
|
SAPI_RAW_VLOG(1, "Inside entries as they appear in chroot:");
|
||||||
|
LogContainer(inside_entries);
|
||||||
|
}
|
||||||
|
|
||||||
// Don't trace the child: it will allow to use 'strace -f' with the whole
|
// Don't trace the child: it will allow to use 'strace -f' with the whole
|
||||||
// sandbox master/monitor, which ptrace_attach'es to the child.
|
// sandbox master/monitor, which ptrace_attach'es to the child.
|
||||||
int clone_flags = CLONE_UNTRACED;
|
int clone_flags = CLONE_UNTRACED;
|
||||||
|
|
|
@ -30,8 +30,6 @@
|
||||||
#include "absl/strings/ascii.h"
|
#include "absl/strings/ascii.h"
|
||||||
#include "absl/strings/match.h"
|
#include "absl/strings/match.h"
|
||||||
#include "absl/strings/str_cat.h"
|
#include "absl/strings/str_cat.h"
|
||||||
#include "absl/strings/str_format.h"
|
|
||||||
#include "absl/strings/str_join.h"
|
|
||||||
#include "absl/strings/str_split.h"
|
#include "absl/strings/str_split.h"
|
||||||
#include "absl/strings/string_view.h"
|
#include "absl/strings/string_view.h"
|
||||||
#include "sandboxed_api/sandbox2/util/fileops.h"
|
#include "sandboxed_api/sandbox2/util/fileops.h"
|
||||||
|
@ -521,4 +519,39 @@ void Mounts::CreateMounts(const std::string& root_path) const {
|
||||||
sandbox2::CreateMounts(mount_tree_, root_path, true);
|
sandbox2::CreateMounts(mount_tree_, root_path, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void RecursivelyListMountsImpl(const MountTree& tree,
|
||||||
|
const std::string& tree_path,
|
||||||
|
std::vector<std::string>* outside_entries,
|
||||||
|
std::vector<std::string>* inside_entries) {
|
||||||
|
const MountTree::Node& node = tree.node();
|
||||||
|
if (node.has_dir_node()) {
|
||||||
|
const char* rw_str = node.dir_node().is_ro() ? "R " : "W ";
|
||||||
|
inside_entries->emplace_back(absl::StrCat(rw_str, tree_path, "/"));
|
||||||
|
outside_entries->emplace_back(absl::StrCat(node.dir_node().outside(), "/"));
|
||||||
|
} else if (node.has_file_node()) {
|
||||||
|
const char* rw_str = node.file_node().is_ro() ? "R " : "W ";
|
||||||
|
inside_entries->emplace_back(absl::StrCat(rw_str, tree_path));
|
||||||
|
outside_entries->emplace_back(absl::StrCat(node.file_node().outside()));
|
||||||
|
} else if (node.has_tmpfs_node()) {
|
||||||
|
outside_entries->emplace_back(
|
||||||
|
absl::StrCat("tmpfs: ", node.tmpfs_node().tmpfs_options()));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& subentry : tree.entries()) {
|
||||||
|
RecursivelyListMountsImpl(subentry.second,
|
||||||
|
absl::StrCat(tree_path, "/", subentry.first),
|
||||||
|
outside_entries, inside_entries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void Mounts::RecursivelyListMounts(std::vector<std::string>* outside_entries,
|
||||||
|
std::vector<std::string>* inside_entries) {
|
||||||
|
RecursivelyListMountsImpl(GetMountTree(), "", outside_entries,
|
||||||
|
inside_entries);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sandbox2
|
} // namespace sandbox2
|
||||||
|
|
|
@ -47,6 +47,16 @@ class Mounts {
|
||||||
|
|
||||||
MountTree GetMountTree() const { return mount_tree_; }
|
MountTree GetMountTree() const { return mount_tree_; }
|
||||||
|
|
||||||
|
// Lists the outside and inside entries of the input tree in the output
|
||||||
|
// parameters, in an ls-like manner. Each entry is traversed in the
|
||||||
|
// depth-first order. However, the entries on the same level of hierarchy are
|
||||||
|
// traversed in their natural order in the tree. The elements in the output
|
||||||
|
// containers match each other pairwise: outside_entries[i] is mounted as
|
||||||
|
// inside_entries[i]. The elements of inside_entries are prefixed with either
|
||||||
|
// 'R' (read-only) or 'W' (writable).
|
||||||
|
void RecursivelyListMounts(std::vector<std::string>* outside_entries,
|
||||||
|
std::vector<std::string>* inside_entries);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class MountTreeTest;
|
friend class MountTreeTest;
|
||||||
::sapi::Status Insert(absl::string_view path, const MountTree::Node& node);
|
::sapi::Status Insert(absl::string_view path, const MountTree::Node& node);
|
||||||
|
|
|
@ -20,8 +20,10 @@
|
||||||
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
#include "absl/strings/match.h"
|
||||||
#include "absl/strings/str_cat.h"
|
#include "absl/strings/str_cat.h"
|
||||||
#include "sandboxed_api/sandbox2/testing.h"
|
#include "sandboxed_api/sandbox2/testing.h"
|
||||||
|
#include "sandboxed_api/sandbox2/util/file_helpers.h"
|
||||||
#include "sandboxed_api/sandbox2/util/path.h"
|
#include "sandboxed_api/sandbox2/util/path.h"
|
||||||
#include "sandboxed_api/sandbox2/util/temp_file.h"
|
#include "sandboxed_api/sandbox2/util/temp_file.h"
|
||||||
#include "sandboxed_api/util/status_matchers.h"
|
#include "sandboxed_api/util/status_matchers.h"
|
||||||
|
@ -29,6 +31,7 @@
|
||||||
using sapi::IsOk;
|
using sapi::IsOk;
|
||||||
using sapi::StatusIs;
|
using sapi::StatusIs;
|
||||||
using ::testing::Eq;
|
using ::testing::Eq;
|
||||||
|
using ::testing::UnorderedElementsAreArray;
|
||||||
|
|
||||||
namespace sandbox2 {
|
namespace sandbox2 {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -166,5 +169,69 @@ TEST(MountTreeTest, TestMinimalDynamicBinary) {
|
||||||
EXPECT_THAT(mounts.AddFile("/lib/x86_64-linux-gnu/libc.so.6"), IsOk());
|
EXPECT_THAT(mounts.AddFile("/lib/x86_64-linux-gnu/libc.so.6"), IsOk());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(MountTreeTest, TestList) {
|
||||||
|
struct TestCase {
|
||||||
|
const char *path;
|
||||||
|
const bool is_ro;
|
||||||
|
};
|
||||||
|
// clang-format off
|
||||||
|
const TestCase test_cases[] = {
|
||||||
|
// NOTE: Directories have a trailing '/'; files don't.
|
||||||
|
{"/a/b", true},
|
||||||
|
{"/a/c/", true},
|
||||||
|
{"/a/c/d/e/f/g", true},
|
||||||
|
{"/h", true},
|
||||||
|
{"/i/j/k", false},
|
||||||
|
{"/i/l/", false},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
Mounts mounts;
|
||||||
|
|
||||||
|
// Create actual directories and files on disk and selectively add
|
||||||
|
for (const auto &test_case : test_cases) {
|
||||||
|
const auto inside_path = test_case.path;
|
||||||
|
const std::string outside_path = absl::StrCat("/some/dir/", inside_path);
|
||||||
|
if (absl::EndsWith(outside_path, "/")) {
|
||||||
|
ASSERT_THAT(
|
||||||
|
mounts.AddDirectoryAt(file::CleanPath(outside_path),
|
||||||
|
file::CleanPath(inside_path), test_case.is_ro),
|
||||||
|
IsOk());
|
||||||
|
} else {
|
||||||
|
ASSERT_THAT(
|
||||||
|
mounts.AddFileAt(file::CleanPath(outside_path),
|
||||||
|
file::CleanPath(inside_path), test_case.is_ro),
|
||||||
|
IsOk());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> outside_entries;
|
||||||
|
std::vector<std::string> inside_entries;
|
||||||
|
mounts.RecursivelyListMounts(&outside_entries, &inside_entries);
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
EXPECT_THAT(
|
||||||
|
inside_entries,
|
||||||
|
UnorderedElementsAreArray({
|
||||||
|
"R /a/b",
|
||||||
|
"R /a/c/",
|
||||||
|
"R /a/c/d/e/f/g",
|
||||||
|
"R /h",
|
||||||
|
"W /i/j/k",
|
||||||
|
"W /i/l/",
|
||||||
|
}));
|
||||||
|
EXPECT_THAT(
|
||||||
|
outside_entries,
|
||||||
|
UnorderedElementsAreArray({
|
||||||
|
absl::StrCat("/some/dir/", "a/b"),
|
||||||
|
absl::StrCat("/some/dir/", "a/c/"),
|
||||||
|
absl::StrCat("/some/dir/", "a/c/d/e/f/g"),
|
||||||
|
absl::StrCat("/some/dir/", "h"),
|
||||||
|
absl::StrCat("/some/dir/", "i/j/k"),
|
||||||
|
absl::StrCat("/some/dir/", "i/l/"),
|
||||||
|
}));
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace sandbox2
|
} // namespace sandbox2
|
||||||
|
|
Loading…
Reference in New Issue
Block a user