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 <syscall.h>
#include <unistd.h>
#include <cerrno>
#include <cstdlib>
@ -42,8 +43,13 @@ using ::sapi::GetTestSourcePath;
using ::testing::Eq;
PolicyBuilder CreatePolicyTestPolicyBuilder() {
return PolicyBuilder()
.DisableNamespaces()
sandbox2::PolicyBuilder builder;
if constexpr (sapi::host_os::IsAndroid()) {
builder.AllowDynamicStartup();
}
builder.DisableNamespaces()
.AllowStaticStartup()
.AllowExit()
.AllowRead()
@ -66,6 +72,7 @@ PolicyBuilder CreatePolicyTestPolicyBuilder() {
},
ENOENT)
.BlockSyscallWithErrno(__NR_prlimit64, EPERM);
return builder;
}
std::unique_ptr<Policy> PolicyTestcasePolicy() {
@ -75,6 +82,7 @@ std::unique_ptr<Policy> PolicyTestcasePolicy() {
#ifdef SAPI_X86_64
// Test that 32-bit syscalls from 64-bit are disallowed.
TEST(PolicyTest, AMD64Syscall32PolicyAllowed) {
SKIP_ANDROID;
SKIP_SANITIZERS_AND_COVERAGE;
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(PolicyTest, AMD64Syscall32FsAllowed) {
SKIP_ANDROID;
SKIP_SANITIZERS_AND_COVERAGE;
const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
std::vector<std::string> args = {path, "2"};
@ -182,20 +191,27 @@ TEST(PolicyTest, IsattyAllowed) {
}
std::unique_ptr<Policy> MinimalTestcasePolicy() {
return PolicyBuilder()
.AllowStaticStartup()
.AllowExit()
sandbox2::PolicyBuilder builder;
if constexpr (sapi::host_os::IsAndroid()) {
builder.AllowDynamicStartup();
builder.DisableNamespaces();
}
builder.AllowStaticStartup()
.BlockSyscallWithErrno(__NR_prlimit64, EPERM)
#ifdef __NR_access
.BlockSyscallWithErrno(__NR_access, ENOENT)
#endif
.BuildOrDie();
.AllowExit();
return builder.BuildOrDie();
}
// Test that we can sandbox a minimal static binary returning 0.
// 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.
TEST(MinimalTest, MinimalBinaryWorks) {
SKIP_ANDROID;
SKIP_SANITIZERS_AND_COVERAGE;
const std::string path = GetTestSourcePath("sandbox2/testcases/minimal");
std::vector<std::string> args = {path};
@ -213,18 +229,24 @@ TEST(MinimalTest, MinimalSharedBinaryWorks) {
GetTestSourcePath("sandbox2/testcases/minimal_dynamic");
std::vector<std::string> args = {path};
auto policy = PolicyBuilder()
.AllowDynamicStartup()
.AllowOpen()
.AllowExit()
.AllowMmap()
sandbox2::PolicyBuilder builder;
if constexpr (sapi::host_os::IsAndroid()) {
builder.DisableNamespaces();
} else {
builder.AddLibrariesForBinary(path);
}
builder.AllowDynamicStartup()
.AllowOpen()
.AllowExit()
.AllowMmap()
#ifdef __NR_access
// New glibc accesses /etc/ld.so.preload
.BlockSyscallWithErrno(__NR_access, ENOENT)
// New glibc accesses /etc/ld.so.preload
.BlockSyscallWithErrno(__NR_access, ENOENT)
#endif
.BlockSyscallWithErrno(__NR_prlimit64, EPERM)
.AddLibrariesForBinary(path)
.BuildOrDie();
.BlockSyscallWithErrno(__NR_prlimit64, EPERM);
auto policy = builder.BuildOrDie();
Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
auto result = s2.Run();
@ -240,15 +262,24 @@ TEST(MallocTest, SystemMallocWorks) {
GetTestSourcePath("sandbox2/testcases/malloc_system");
std::vector<std::string> args = {path};
auto policy = PolicyBuilder()
.AllowStaticStartup()
.AllowSystemMalloc()
.AllowExit()
.BlockSyscallWithErrno(__NR_prlimit64, EPERM)
sandbox2::PolicyBuilder builder;
if constexpr (sapi::host_os::IsAndroid()) {
builder.DisableNamespaces();
builder.AllowDynamicStartup();
builder.AllowSyscalls({
__NR_madvise,
});
}
builder.AllowStaticStartup()
.AllowSystemMalloc()
.BlockSyscallWithErrno(__NR_prlimit64, EPERM)
#ifdef __NR_access
.BlockSyscallWithErrno(__NR_access, ENOENT)
.BlockSyscallWithErrno(__NR_access, ENOENT)
#endif
.BuildOrDie();
.AllowExit();
auto policy = builder.BuildOrDie();
Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
auto result = s2.Run();
@ -268,57 +299,62 @@ TEST(MultipleSyscalls, AddPolicyOnSyscallsWorks) {
GetTestSourcePath("sandbox2/testcases/add_policy_on_syscalls");
std::vector<std::string> args = {path};
auto policy = PolicyBuilder()
.AllowStaticStartup()
.AllowTcMalloc()
.AllowExit()
.AddPolicyOnSyscalls(
{
__NR_getuid,
__NR_getgid,
__NR_geteuid,
__NR_getegid,
sandbox2::PolicyBuilder builder;
if constexpr (sapi::host_os::IsAndroid()) {
builder.DisableNamespaces();
builder.AllowDynamicStartup();
}
builder.AllowStaticStartup()
.AllowTcMalloc()
.AllowExit()
.AddPolicyOnSyscalls(
{
__NR_getuid,
__NR_getgid,
__NR_geteuid,
__NR_getegid,
#ifdef __NR_getuid32
__NR_getuid32,
__NR_getuid32,
#endif
#ifdef __NR_getgid32
__NR_getgid32,
__NR_getgid32,
#endif
#ifdef __NR_geteuid32
__NR_geteuid32,
__NR_geteuid32,
#endif
#ifdef __NR_getegid32
__NR_getegid32,
__NR_getegid32,
#endif
},
{ALLOW})
.AddPolicyOnSyscalls(
{
__NR_getresuid,
__NR_getresgid,
},
{ALLOW})
.AddPolicyOnSyscalls(
{
__NR_getresuid,
__NR_getresgid,
#ifdef __NR_getresuid32
__NR_getresuid32,
__NR_getresuid32,
#endif
#ifdef __NR_getresgid32
__NR_getresgid32,
__NR_getresgid32,
#endif
},
{ERRNO(42)})
.AddPolicyOnSyscalls({__NR_read, __NR_write}, {ERRNO(43)})
.AddPolicyOnSyscall(__NR_umask, {DENY})
.BlockSyscallsWithErrno(
{
},
{ERRNO(42)})
.AddPolicyOnSyscalls({__NR_read, __NR_write}, {ERRNO(43)})
.AddPolicyOnSyscall(__NR_umask, {DENY})
.BlockSyscallsWithErrno(
{
#ifdef __NR_open
__NR_open,
__NR_open,
#endif
__NR_openat,
__NR_openat,
#ifdef __NR_access
__NR_access,
__NR_access,
#endif
},
ENOENT)
.BlockSyscallWithErrno(__NR_prlimit64, EPERM)
.BuildOrDie();
},
ENOENT)
.BlockSyscallWithErrno(__NR_prlimit64, EPERM);
auto policy = builder.BuildOrDie();
Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
auto result = s2.Run();

View File

@ -724,6 +724,77 @@ PolicyBuilder& PolicyBuilder::AllowStaticStartup() {
}
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();
AllowStat();

View File

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

View File

@ -117,26 +117,6 @@ TEST(ExecutorTest, ExecutorFdConstructor) {
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
// external signal. Also make sure that we do not have the stack trace.
TEST(RunAsyncTest, SandboxeeExternalKill) {
@ -162,6 +142,7 @@ TEST(RunAsyncTest, SandboxeeExternalKill) {
// Tests that we return the correct state when the sandboxee timed out.
TEST(RunAsyncTest, SandboxeeTimeoutWithStacktraces) {
SKIP_ANDROID;
const std::string path = GetTestSourcePath("sandbox2/testcases/sleep");
std::vector<std::string> args = {path};
@ -193,28 +174,6 @@ TEST(RunAsyncTest, SandboxeeTimeoutDisabledStacktraces) {
PolicyBuilder()
// Don't restrict the syscalls at all.
.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());
Sandbox2 sandbox(std::move(executor), std::move(policy));
ASSERT_TRUE(sandbox.RunAsync());

View File

@ -20,6 +20,22 @@
#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
// a given test (by emitting 'return') when running under one of the sanitizers
// (ASan, MSan, TSan) or under code coverage. Example: