This change introduces internal experimental support for Android.

PiperOrigin-RevId: 453669315
Change-Id: I6c3278804071caa2bb347cfeb584975339cb50d5
This commit is contained in:
Oliver Kunz 2022-06-08 06:51:02 -07:00 committed by Copybara-Service
parent a8a558c66d
commit 598b00103a
5 changed files with 204 additions and 105 deletions

View File

@ -16,6 +16,7 @@
#include <sys/resource.h> #include <sys/resource.h>
#include <syscall.h> #include <syscall.h>
#include <unistd.h>
#include <cerrno> #include <cerrno>
#include <cstdlib> #include <cstdlib>
@ -42,8 +43,13 @@ using ::sapi::GetTestSourcePath;
using ::testing::Eq; using ::testing::Eq;
PolicyBuilder CreatePolicyTestPolicyBuilder() { PolicyBuilder CreatePolicyTestPolicyBuilder() {
return PolicyBuilder() sandbox2::PolicyBuilder builder;
.DisableNamespaces()
if constexpr (sapi::host_os::IsAndroid()) {
builder.AllowDynamicStartup();
}
builder.DisableNamespaces()
.AllowStaticStartup() .AllowStaticStartup()
.AllowExit() .AllowExit()
.AllowRead() .AllowRead()
@ -66,6 +72,7 @@ PolicyBuilder CreatePolicyTestPolicyBuilder() {
}, },
ENOENT) ENOENT)
.BlockSyscallWithErrno(__NR_prlimit64, EPERM); .BlockSyscallWithErrno(__NR_prlimit64, EPERM);
return builder;
} }
std::unique_ptr<Policy> PolicyTestcasePolicy() { std::unique_ptr<Policy> PolicyTestcasePolicy() {
@ -75,6 +82,7 @@ std::unique_ptr<Policy> PolicyTestcasePolicy() {
#ifdef SAPI_X86_64 #ifdef SAPI_X86_64
// Test that 32-bit syscalls from 64-bit are disallowed. // Test that 32-bit syscalls from 64-bit are disallowed.
TEST(PolicyTest, AMD64Syscall32PolicyAllowed) { TEST(PolicyTest, AMD64Syscall32PolicyAllowed) {
SKIP_ANDROID;
SKIP_SANITIZERS_AND_COVERAGE; SKIP_SANITIZERS_AND_COVERAGE;
const std::string path = GetTestSourcePath("sandbox2/testcases/policy"); const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
@ -89,6 +97,7 @@ TEST(PolicyTest, AMD64Syscall32PolicyAllowed) {
// Test that 32-bit syscalls from 64-bit for FS checks are disallowed. // Test that 32-bit syscalls from 64-bit for FS checks are disallowed.
TEST(PolicyTest, AMD64Syscall32FsAllowed) { TEST(PolicyTest, AMD64Syscall32FsAllowed) {
SKIP_ANDROID;
SKIP_SANITIZERS_AND_COVERAGE; SKIP_SANITIZERS_AND_COVERAGE;
const std::string path = GetTestSourcePath("sandbox2/testcases/policy"); const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
std::vector<std::string> args = {path, "2"}; std::vector<std::string> args = {path, "2"};
@ -182,20 +191,27 @@ TEST(PolicyTest, IsattyAllowed) {
} }
std::unique_ptr<Policy> MinimalTestcasePolicy() { std::unique_ptr<Policy> MinimalTestcasePolicy() {
return PolicyBuilder() sandbox2::PolicyBuilder builder;
.AllowStaticStartup()
.AllowExit() if constexpr (sapi::host_os::IsAndroid()) {
builder.AllowDynamicStartup();
builder.DisableNamespaces();
}
builder.AllowStaticStartup()
.BlockSyscallWithErrno(__NR_prlimit64, EPERM) .BlockSyscallWithErrno(__NR_prlimit64, EPERM)
#ifdef __NR_access #ifdef __NR_access
.BlockSyscallWithErrno(__NR_access, ENOENT) .BlockSyscallWithErrno(__NR_access, ENOENT)
#endif #endif
.BuildOrDie(); .AllowExit();
return builder.BuildOrDie();
} }
// Test that we can sandbox a minimal static binary returning 0. // Test that we can sandbox a minimal static binary returning 0.
// If this starts failing, it means something changed, maybe in the way we // If this starts failing, it means something changed, maybe in the way we
// compile static binaries, and we need to update the policy just above. // compile static binaries, and we need to update the policy just above.
TEST(MinimalTest, MinimalBinaryWorks) { TEST(MinimalTest, MinimalBinaryWorks) {
SKIP_ANDROID;
SKIP_SANITIZERS_AND_COVERAGE; SKIP_SANITIZERS_AND_COVERAGE;
const std::string path = GetTestSourcePath("sandbox2/testcases/minimal"); const std::string path = GetTestSourcePath("sandbox2/testcases/minimal");
std::vector<std::string> args = {path}; std::vector<std::string> args = {path};
@ -213,8 +229,15 @@ TEST(MinimalTest, MinimalSharedBinaryWorks) {
GetTestSourcePath("sandbox2/testcases/minimal_dynamic"); GetTestSourcePath("sandbox2/testcases/minimal_dynamic");
std::vector<std::string> args = {path}; std::vector<std::string> args = {path};
auto policy = PolicyBuilder() sandbox2::PolicyBuilder builder;
.AllowDynamicStartup()
if constexpr (sapi::host_os::IsAndroid()) {
builder.DisableNamespaces();
} else {
builder.AddLibrariesForBinary(path);
}
builder.AllowDynamicStartup()
.AllowOpen() .AllowOpen()
.AllowExit() .AllowExit()
.AllowMmap() .AllowMmap()
@ -222,9 +245,8 @@ TEST(MinimalTest, MinimalSharedBinaryWorks) {
// New glibc accesses /etc/ld.so.preload // New glibc accesses /etc/ld.so.preload
.BlockSyscallWithErrno(__NR_access, ENOENT) .BlockSyscallWithErrno(__NR_access, ENOENT)
#endif #endif
.BlockSyscallWithErrno(__NR_prlimit64, EPERM) .BlockSyscallWithErrno(__NR_prlimit64, EPERM);
.AddLibrariesForBinary(path) auto policy = builder.BuildOrDie();
.BuildOrDie();
Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy)); Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
auto result = s2.Run(); auto result = s2.Run();
@ -240,15 +262,24 @@ TEST(MallocTest, SystemMallocWorks) {
GetTestSourcePath("sandbox2/testcases/malloc_system"); GetTestSourcePath("sandbox2/testcases/malloc_system");
std::vector<std::string> args = {path}; std::vector<std::string> args = {path};
auto policy = PolicyBuilder() sandbox2::PolicyBuilder builder;
.AllowStaticStartup()
if constexpr (sapi::host_os::IsAndroid()) {
builder.DisableNamespaces();
builder.AllowDynamicStartup();
builder.AllowSyscalls({
__NR_madvise,
});
}
builder.AllowStaticStartup()
.AllowSystemMalloc() .AllowSystemMalloc()
.AllowExit()
.BlockSyscallWithErrno(__NR_prlimit64, EPERM) .BlockSyscallWithErrno(__NR_prlimit64, EPERM)
#ifdef __NR_access #ifdef __NR_access
.BlockSyscallWithErrno(__NR_access, ENOENT) .BlockSyscallWithErrno(__NR_access, ENOENT)
#endif #endif
.BuildOrDie(); .AllowExit();
auto policy = builder.BuildOrDie();
Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy)); Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
auto result = s2.Run(); auto result = s2.Run();
@ -268,8 +299,13 @@ TEST(MultipleSyscalls, AddPolicyOnSyscallsWorks) {
GetTestSourcePath("sandbox2/testcases/add_policy_on_syscalls"); GetTestSourcePath("sandbox2/testcases/add_policy_on_syscalls");
std::vector<std::string> args = {path}; std::vector<std::string> args = {path};
auto policy = PolicyBuilder() sandbox2::PolicyBuilder builder;
.AllowStaticStartup() if constexpr (sapi::host_os::IsAndroid()) {
builder.DisableNamespaces();
builder.AllowDynamicStartup();
}
builder.AllowStaticStartup()
.AllowTcMalloc() .AllowTcMalloc()
.AllowExit() .AllowExit()
.AddPolicyOnSyscalls( .AddPolicyOnSyscalls(
@ -317,8 +353,8 @@ TEST(MultipleSyscalls, AddPolicyOnSyscallsWorks) {
#endif #endif
}, },
ENOENT) ENOENT)
.BlockSyscallWithErrno(__NR_prlimit64, EPERM) .BlockSyscallWithErrno(__NR_prlimit64, EPERM);
.BuildOrDie(); auto policy = builder.BuildOrDie();
Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy)); Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
auto result = s2.Run(); auto result = s2.Run();

View File

@ -724,6 +724,77 @@ PolicyBuilder& PolicyBuilder::AllowStaticStartup() {
} }
PolicyBuilder& PolicyBuilder::AllowDynamicStartup() { PolicyBuilder& PolicyBuilder::AllowDynamicStartup() {
#ifdef __ANDROID__
AllowAccess();
AllowSafeFcntl();
AllowGetIDs();
AllowGetPIDs();
AllowGetRandom();
AllowOpen();
AllowSyscalls({
#ifdef __NR_fstatfs
__NR_fstatfs,
#endif
#ifdef __NR_fstatfs64
__NR_fstatfs64,
#endif
__NR_readlinkat,
__NR_sched_getaffinity,
__NR_sched_getscheduler,
});
AllowHandleSignals();
AllowFutexOp(FUTEX_WAKE_PRIVATE);
AddPolicyOnSyscall(__NR_prctl,
[](bpf_labels& labels) -> std::vector<sock_filter> {
return {
ARG_32(0), // option
JEQ32(PR_GET_DUMPABLE, ALLOW),
JNE32(PR_SET_VMA, JUMP(&labels, prctl_end)),
ARG_32(1), // arg2
JEQ32(PR_SET_VMA_ANON_NAME, ALLOW),
LABEL(&labels, prctl_end),
};
});
AddPolicyOnSyscall(__NR_mremap,
{
ARG_32(3),
JEQ32(MREMAP_MAYMOVE | MREMAP_FIXED, ALLOW),
});
AddPolicyOnMmap([](bpf_labels& labels) -> std::vector<sock_filter> {
return {
ARG_32(2), // prot
JEQ32(PROT_NONE, JUMP(&labels, prot_none)),
JEQ32(PROT_READ, JUMP(&labels, prot_read)),
JEQ32(PROT_READ | PROT_WRITE, JUMP(&labels, prot_RW_or_RX)),
JEQ32(PROT_READ | PROT_EXEC, JUMP(&labels, prot_RW_or_RX)),
// PROT_NONE
LABEL(&labels, prot_none),
ARG_32(3), // flags
JEQ32(MAP_PRIVATE | MAP_ANONYMOUS, ALLOW),
JEQ32(MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, ALLOW),
JUMP(&labels, mmap_end),
// PROT_READ
LABEL(&labels, prot_read),
ARG_32(3), // flags
JEQ32(MAP_SHARED, ALLOW),
JEQ32(MAP_PRIVATE, ALLOW),
JEQ32(MAP_PRIVATE | MAP_FIXED, ALLOW),
JUMP(&labels, mmap_end),
// PROT_READ | PROT_WRITE
// PROT_READ | PROT_EXEC
LABEL(&labels, prot_RW_or_RX),
ARG_32(3), // flags
JEQ32(MAP_PRIVATE | MAP_FIXED, ALLOW),
LABEL(&labels, mmap_end),
};
});
#endif
AllowRead(); AllowRead();
AllowStat(); AllowStat();

View File

@ -73,7 +73,8 @@ using ::sapi::StatusIs;
class PolicyBuilderTest : public testing::Test { class PolicyBuilderTest : public testing::Test {
protected: protected:
static std::string Run(std::vector<std::string> args, bool network = false); static std::string Run(const std::vector<std::string>& args,
bool network = false);
}; };
TEST_F(PolicyBuilderTest, Testpolicy_size) { TEST_F(PolicyBuilderTest, Testpolicy_size) {
@ -157,12 +158,18 @@ TEST_F(PolicyBuilderTest, TestValidateAbsolutePath) {
} }
} }
std::string PolicyBuilderTest::Run(std::vector<std::string> args, std::string PolicyBuilderTest::Run(const std::vector<std::string>& args,
bool network) { bool network) {
PolicyBuilder builder; PolicyBuilder builder;
// Don't restrict the syscalls at all. // Don't restrict the syscalls at all.
builder.DangerDefaultAllowAll(); builder.DangerDefaultAllowAll();
if constexpr (sapi::host_os::IsAndroid()) {
builder.DisableNamespaces();
} else {
builder.AddLibrariesForBinary(args[0]); builder.AddLibrariesForBinary(args[0]);
}
if (network) { if (network) {
builder.AllowUnrestrictedNetworking(); builder.AllowUnrestrictedNetworking();
} }
@ -216,6 +223,7 @@ TEST_F(PolicyBuilderTest, TestEcho) {
} }
TEST_F(PolicyBuilderTest, TestInterfacesNoNetwork) { TEST_F(PolicyBuilderTest, TestInterfacesNoNetwork) {
SKIP_ANDROID;
auto lines = absl::StrSplit(Run({"/sbin/ip", "addr", "show", "up"}), '\n'); auto lines = absl::StrSplit(Run({"/sbin/ip", "addr", "show", "up"}), '\n');
int count = 0; int count = 0;
@ -230,6 +238,7 @@ TEST_F(PolicyBuilderTest, TestInterfacesNoNetwork) {
} }
TEST_F(PolicyBuilderTest, TestInterfacesNetwork) { TEST_F(PolicyBuilderTest, TestInterfacesNetwork) {
SKIP_ANDROID;
auto lines = auto lines =
absl::StrSplit(Run({"/sbin/ip", "addr", "show", "up"}, true), '\n'); absl::StrSplit(Run({"/sbin/ip", "addr", "show", "up"}, true), '\n');
@ -245,11 +254,19 @@ TEST_F(PolicyBuilderTest, TestInterfacesNetwork) {
} }
TEST_F(PolicyBuilderTest, TestUid) { TEST_F(PolicyBuilderTest, TestUid) {
if constexpr (!sapi::host_os::IsAndroid()) {
EXPECT_THAT(Run({"/usr/bin/id", "-u"}), StrEq("1000\n")); EXPECT_THAT(Run({"/usr/bin/id", "-u"}), StrEq("1000\n"));
} else {
EXPECT_THAT(Run({"/bin/id", "-u"}), StrEq("0\n"));
}
} }
TEST_F(PolicyBuilderTest, TestGid) { TEST_F(PolicyBuilderTest, TestGid) {
if constexpr (!sapi::host_os::IsAndroid()) {
EXPECT_THAT(Run({"/usr/bin/id", "-g"}), StrEq("1000\n")); EXPECT_THAT(Run({"/usr/bin/id", "-g"}), StrEq("1000\n"));
} else {
EXPECT_THAT(Run({"/bin/id", "-g"}), StrEq("0\n"));
}
} }
TEST_F(PolicyBuilderTest, TestOpenFds) { TEST_F(PolicyBuilderTest, TestOpenFds) {

View File

@ -117,26 +117,6 @@ TEST(ExecutorTest, ExecutorFdConstructor) {
ASSERT_EQ(result.final_status(), Result::OK); ASSERT_EQ(result.final_status(), Result::OK);
} }
TEST(StackTraceTest, StackTraceOnExitWorks) {
SKIP_SANITIZERS_AND_COVERAGE;
const std::string path = GetTestSourcePath("sandbox2/testcases/minimal");
std::vector<std::string> args = {path};
auto executor = absl::make_unique<Executor>(path, args);
SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
PolicyBuilder()
// Don't restrict the syscalls at all.
.DangerDefaultAllowAll()
.CollectStacktracesOnExit(true)
.TryBuild());
Sandbox2 sandbox(std::move(executor), std::move(policy));
auto result = sandbox.Run();
ASSERT_EQ(result.final_status(), Result::OK);
EXPECT_THAT(result.stack_trace(), Not(IsEmpty()));
}
// Tests that we return the correct state when the sandboxee was killed by an // Tests that we return the correct state when the sandboxee was killed by an
// external signal. Also make sure that we do not have the stack trace. // external signal. Also make sure that we do not have the stack trace.
TEST(RunAsyncTest, SandboxeeExternalKill) { TEST(RunAsyncTest, SandboxeeExternalKill) {
@ -162,6 +142,7 @@ TEST(RunAsyncTest, SandboxeeExternalKill) {
// Tests that we return the correct state when the sandboxee timed out. // Tests that we return the correct state when the sandboxee timed out.
TEST(RunAsyncTest, SandboxeeTimeoutWithStacktraces) { TEST(RunAsyncTest, SandboxeeTimeoutWithStacktraces) {
SKIP_ANDROID;
const std::string path = GetTestSourcePath("sandbox2/testcases/sleep"); const std::string path = GetTestSourcePath("sandbox2/testcases/sleep");
std::vector<std::string> args = {path}; std::vector<std::string> args = {path};
@ -193,28 +174,6 @@ TEST(RunAsyncTest, SandboxeeTimeoutDisabledStacktraces) {
PolicyBuilder() PolicyBuilder()
// Don't restrict the syscalls at all. // Don't restrict the syscalls at all.
.DangerDefaultAllowAll() .DangerDefaultAllowAll()
.CollectStacktracesOnTimeout(false)
.TryBuild());
Sandbox2 sandbox(std::move(executor), std::move(policy));
ASSERT_TRUE(sandbox.RunAsync());
sandbox.set_walltime_limit(absl::Seconds(1));
auto result = sandbox.AwaitResult();
EXPECT_EQ(result.final_status(), Result::TIMEOUT);
EXPECT_THAT(result.GetStackTrace(), IsEmpty());
}
// Tests that we do not collect stack traces if it was disabled (violation).
TEST(RunAsyncTest, SandboxeeViolationDisabledStacktraces) {
const std::string path = GetTestSourcePath("sandbox2/testcases/sleep");
std::vector<std::string> args = {path};
std::vector<std::string> envs;
auto executor = absl::make_unique<Executor>(path, args, envs);
SAPI_ASSERT_OK_AND_ASSIGN(
auto policy, PolicyBuilder()
// Don't allow anything - Make sure that we'll crash.
.CollectStacktracesOnViolation(false)
.TryBuild()); .TryBuild());
Sandbox2 sandbox(std::move(executor), std::move(policy)); Sandbox2 sandbox(std::move(executor), std::move(policy));
ASSERT_TRUE(sandbox.RunAsync()); ASSERT_TRUE(sandbox.RunAsync());

View File

@ -20,6 +20,22 @@
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
// The macro SKIP_ANDROID can be used in tests to skip running a
// given test (by emitting 'retrun') when running on Android. Example:
//
// TEST(Foo, Bar) {
// SKIP_ANDROID;
// [...]
// }
//
// The reason for this is because certain unit tests require the use of user
// namespaces which are not present on Android.
#if defined(__ANDROID__)
#define SKIP_ANDROID return
#else
#define SKIP_ANDROID
#endif
// The macro SKIP_SANITIZERS_AND_COVERAGE can be used in tests to skip running // The macro SKIP_SANITIZERS_AND_COVERAGE can be used in tests to skip running
// a given test (by emitting 'return') when running under one of the sanitizers // a given test (by emitting 'return') when running under one of the sanitizers
// (ASan, MSan, TSan) or under code coverage. Example: // (ASan, MSan, TSan) or under code coverage. Example: