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:file_base",
|
||||
"//sandboxed_api/sandbox2/util:fileops",
|
||||
"//sandboxed_api/util:raw_logging",
|
||||
"//sandboxed_api/util:status",
|
||||
"//sandboxed_api/util:statusor",
|
||||
|
||||
|
@ -377,7 +378,6 @@ cc_library(
|
|||
"@com_google_absl//absl/base:core_headers",
|
||||
"@com_google_absl//absl/container:flat_hash_set",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/strings:str_format",
|
||||
"@com_google_protobuf//:protobuf",
|
||||
],
|
||||
)
|
||||
|
@ -390,6 +390,7 @@ cc_test(
|
|||
":mounts",
|
||||
":testing",
|
||||
"//sandboxed_api/sandbox2/util:file_base",
|
||||
"//sandboxed_api/sandbox2/util:file_helpers",
|
||||
"//sandboxed_api/sandbox2/util:temp_file",
|
||||
"//sandboxed_api/util:status_matchers",
|
||||
"@com_google_absl//absl/strings",
|
||||
|
|
|
@ -16,9 +16,10 @@
|
|||
|
||||
#include "sandboxed_api/sandbox2/monitor.h"
|
||||
|
||||
// clang-format off
|
||||
#include <linux/posix_types.h> // NOLINT: Needs to come before linux/ipc.h
|
||||
|
||||
#include <linux/ipc.h>
|
||||
// clang-format on
|
||||
#include <sched.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ptrace.h>
|
||||
|
@ -59,6 +60,7 @@
|
|||
#include "sandboxed_api/sandbox2/stack-trace.h"
|
||||
#include "sandboxed_api/sandbox2/syscall.h"
|
||||
#include "sandboxed_api/sandbox2/util.h"
|
||||
#include "sandboxed_api/util/raw_logging.h"
|
||||
|
||||
ABSL_FLAG(bool, sandbox2_report_on_sandboxee_signal, true,
|
||||
"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() {
|
||||
using DecrementCounter = decltype(setup_counter_);
|
||||
std::unique_ptr<DecrementCounter, std::function<void(DecrementCounter*)>>
|
||||
|
@ -139,6 +151,18 @@ void Monitor::Run() {
|
|||
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
|
||||
// sandbox master/monitor, which ptrace_attach'es to the child.
|
||||
int clone_flags = CLONE_UNTRACED;
|
||||
|
|
|
@ -30,8 +30,6 @@
|
|||
#include "absl/strings/ascii.h"
|
||||
#include "absl/strings/match.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/string_view.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);
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -47,6 +47,16 @@ class Mounts {
|
|||
|
||||
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:
|
||||
friend class MountTreeTest;
|
||||
::sapi::Status Insert(absl::string_view path, const MountTree::Node& node);
|
||||
|
|
|
@ -20,8 +20,10 @@
|
|||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/str_cat.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/temp_file.h"
|
||||
#include "sandboxed_api/util/status_matchers.h"
|
||||
|
@ -29,6 +31,7 @@
|
|||
using sapi::IsOk;
|
||||
using sapi::StatusIs;
|
||||
using ::testing::Eq;
|
||||
using ::testing::UnorderedElementsAreArray;
|
||||
|
||||
namespace sandbox2 {
|
||||
namespace {
|
||||
|
@ -166,5 +169,69 @@ TEST(MountTreeTest, TestMinimalDynamicBinary) {
|
|||
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 sandbox2
|
||||
|
|
Loading…
Reference in New Issue
Block a user