Run more tests with coverage enabled

PiperOrigin-RevId: 561575508
Change-Id: Ifc9a678b6a6cbcd892a1f8710b941514eb1d9764
pull/171/head
Wiktor Garbacz 2023-08-31 00:43:30 -07:00 committed by Copybara-Service
parent 47c868e6b1
commit f715bd8ba9
9 changed files with 124 additions and 62 deletions

View File

@ -23,9 +23,12 @@ exports_files(["LICENSE"])
cc_library( cc_library(
name = "config", name = "config",
srcs = ["config.cc"],
hdrs = ["config.h"], hdrs = ["config.h"],
copts = sapi_platform_copts(), copts = sapi_platform_copts(),
deps = ["@com_google_absl//absl/base:config"], deps = [
"@com_google_absl//absl/base:config",
],
) )
sapi_proto_library( sapi_proto_library(

View File

@ -22,12 +22,13 @@ add_subdirectory(examples)
# sandboxed_api:config # sandboxed_api:config
add_library(sapi_config ${SAPI_LIB_TYPE} add_library(sapi_config ${SAPI_LIB_TYPE}
config.cc
config.h config.h
) )
add_library(sapi::config ALIAS sapi_config) add_library(sapi::config ALIAS sapi_config)
target_link_libraries(sapi_config target_link_libraries(sapi_config
PRIVATE sapi::base PRIVATE sapi::base
INTERFACE absl::config PUBLIC absl::config
) )
# sandboxed_api:proto_arg # sandboxed_api:proto_arg

11
sandboxed_api/config.cc Normal file
View File

@ -0,0 +1,11 @@
#include "sandboxed_api/config.h"
#include <cstdlib>
namespace sapi {
bool IsCoverageRun() {
return getenv("COVERAGE") != nullptr;
}
} // namespace sapi

View File

@ -17,9 +17,8 @@
#include <features.h> #include <features.h>
#include <cstdint> #include <cstdint>
#include <string>
#include "absl/base/config.h" #include "absl/base/config.h" // IWYU pragma: keep
// GCC/Clang define __x86_64__, Visual Studio uses _M_X64 // GCC/Clang define __x86_64__, Visual Studio uses _M_X64
#if defined(__x86_64__) || defined(_M_X64) #if defined(__x86_64__) || defined(_M_X64)
@ -43,6 +42,9 @@
namespace sapi { namespace sapi {
// Returns whether the executable running under code coverage.
bool IsCoverageRun();
namespace cpu { namespace cpu {
// CPU architectures known to Sandbox2 // CPU architectures known to Sandbox2

View File

@ -136,7 +136,7 @@ TEST(PolicyTest, BpfPtracePermissionDenied) {
} }
TEST(PolicyTest, IsattyAllowed) { TEST(PolicyTest, IsattyAllowed) {
SKIP_SANITIZERS_AND_COVERAGE; SKIP_SANITIZERS;
sandbox2::PolicyBuilder builder; sandbox2::PolicyBuilder builder;
if constexpr (sapi::host_os::IsAndroid()) { if constexpr (sapi::host_os::IsAndroid()) {
builder.DisableNamespaces().AllowDynamicStartup(); builder.DisableNamespaces().AllowDynamicStartup();
@ -145,7 +145,8 @@ TEST(PolicyTest, IsattyAllowed) {
.AllowExit() .AllowExit()
.AllowRead() .AllowRead()
.AllowWrite() .AllowWrite()
.AllowTCGETS(); .AllowTCGETS()
.AllowLlvmCoverage();
const std::string path = GetTestSourcePath("sandbox2/testcases/policy"); const std::string path = GetTestSourcePath("sandbox2/testcases/policy");
std::vector<std::string> args = {path, "6"}; std::vector<std::string> args = {path, "6"};
SAPI_ASSERT_OK_AND_ASSIGN(auto policy, builder.TryBuild()); SAPI_ASSERT_OK_AND_ASSIGN(auto policy, builder.TryBuild());
@ -155,7 +156,7 @@ TEST(PolicyTest, IsattyAllowed) {
ASSERT_THAT(result.final_status(), Eq(Result::OK)); ASSERT_THAT(result.final_status(), Eq(Result::OK));
} }
std::unique_ptr<Policy> MinimalTestcasePolicy() { std::unique_ptr<Policy> MinimalTestcasePolicy(absl::string_view path = "") {
sandbox2::PolicyBuilder builder; sandbox2::PolicyBuilder builder;
if constexpr (sapi::host_os::IsAndroid()) { if constexpr (sapi::host_os::IsAndroid()) {
@ -163,7 +164,7 @@ std::unique_ptr<Policy> MinimalTestcasePolicy() {
builder.DisableNamespaces(); builder.DisableNamespaces();
} }
builder.AllowStaticStartup().AllowExit(); builder.AllowStaticStartup().AllowExit().AllowLlvmCoverage();
return builder.BuildOrDie(); return builder.BuildOrDie();
} }
@ -172,10 +173,11 @@ std::unique_ptr<Policy> MinimalTestcasePolicy() {
// 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_ANDROID;
SKIP_SANITIZERS_AND_COVERAGE; SKIP_SANITIZERS;
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};
Sandbox2 s2(std::make_unique<Executor>(path, args), MinimalTestcasePolicy()); Sandbox2 s2(std::make_unique<Executor>(path, args),
MinimalTestcasePolicy(path));
auto result = s2.Run(); auto result = s2.Run();
ASSERT_THAT(result.final_status(), Eq(Result::OK)); ASSERT_THAT(result.final_status(), Eq(Result::OK));
@ -184,7 +186,7 @@ TEST(MinimalTest, MinimalBinaryWorks) {
// Test that we can sandbox a minimal non-static binary returning 0. // Test that we can sandbox a minimal non-static binary returning 0.
TEST(MinimalTest, MinimalSharedBinaryWorks) { TEST(MinimalTest, MinimalSharedBinaryWorks) {
SKIP_SANITIZERS_AND_COVERAGE; SKIP_SANITIZERS;
const std::string path = const std::string path =
GetTestSourcePath("sandbox2/testcases/minimal_dynamic"); GetTestSourcePath("sandbox2/testcases/minimal_dynamic");
std::vector<std::string> args = {path}; std::vector<std::string> args = {path};
@ -197,7 +199,7 @@ TEST(MinimalTest, MinimalSharedBinaryWorks) {
builder.AddLibrariesForBinary(path); builder.AddLibrariesForBinary(path);
} }
builder.AllowDynamicStartup().AllowExit(); builder.AllowDynamicStartup().AllowExit().AllowLlvmCoverage();
auto policy = builder.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));
@ -209,7 +211,7 @@ TEST(MinimalTest, MinimalSharedBinaryWorks) {
// Test that the AllowSystemMalloc helper works as expected. // Test that the AllowSystemMalloc helper works as expected.
TEST(MallocTest, SystemMallocWorks) { TEST(MallocTest, SystemMallocWorks) {
SKIP_SANITIZERS_AND_COVERAGE; SKIP_SANITIZERS;
const std::string path = const std::string path =
GetTestSourcePath("sandbox2/testcases/malloc_system"); GetTestSourcePath("sandbox2/testcases/malloc_system");
std::vector<std::string> args = {path}; std::vector<std::string> args = {path};
@ -224,7 +226,10 @@ TEST(MallocTest, SystemMallocWorks) {
}); });
} }
builder.AllowStaticStartup().AllowSystemMalloc().AllowExit(); builder.AllowStaticStartup()
.AllowSystemMalloc()
.AllowExit()
.AllowLlvmCoverage();
auto policy = builder.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));

View File

@ -34,6 +34,7 @@
#include <cerrno> #include <cerrno>
#include <csignal> #include <csignal>
#include <cstdint> #include <cstdint>
#include <cstdlib>
#include <deque> #include <deque>
#include <functional> #include <functional>
#include <iterator> #include <iterator>
@ -327,47 +328,49 @@ PolicyBuilder& PolicyBuilder::AllowSystemMalloc() {
} }
PolicyBuilder& PolicyBuilder::AllowLlvmSanitizers() { PolicyBuilder& PolicyBuilder::AllowLlvmSanitizers() {
if constexpr (sapi::sanitizers::IsAny()) { if constexpr (!sapi::sanitizers::IsAny()) {
// *san use a custom allocator that runs mmap/unmap under the hood. For return *this;
// example:
// https://github.com/llvm/llvm-project/blob/596d534ac3524052df210be8d3c01a33b2260a42/compiler-rt/lib/asan/asan_allocator.cpp#L980
// https://github.com/llvm/llvm-project/blob/62ec4ac90738a5f2d209ed28c822223e58aaaeb7/compiler-rt/lib/sanitizer_common/sanitizer_allocator_secondary.h#L98
AllowMmap();
AllowSyscall(__NR_munmap);
// https://github.com/llvm/llvm-project/blob/4bbc3290a25c0dc26007912a96e0f77b2092ee56/compiler-rt/lib/sanitizer_common/sanitizer_stack_store.cpp#L293
AddPolicyOnSyscall(__NR_mprotect,
{
ARG_32(2),
BPF_STMT(BPF_AND | BPF_ALU | BPF_K,
~uint32_t{PROT_READ | PROT_WRITE}),
JEQ32(PROT_NONE, ALLOW),
});
AddPolicyOnSyscall(__NR_madvise, {
ARG_32(2),
JEQ32(MADV_DONTDUMP, ALLOW),
JEQ32(MADV_NOHUGEPAGE, ALLOW),
});
// Sanitizers read from /proc. For example:
// https://github.com/llvm/llvm-project/blob/634da7a1c61ee8c173e90a841eb1f4ea03caa20b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp#L1155
AddDirectoryIfNamespaced("/proc");
AllowOpen();
// Sanitizers need pid for reports. For example:
// https://github.com/llvm/llvm-project/blob/634da7a1c61ee8c173e90a841eb1f4ea03caa20b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp#L740
AllowGetPIDs();
// Sanitizers may try color output. For example:
// https://github.com/llvm/llvm-project/blob/87dd3d350c4ce0115b2cdf91d85ddd05ae2661aa/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp#L157
OverridableBlockSyscallWithErrno(__NR_ioctl, EPERM);
// https://github.com/llvm/llvm-project/blob/9aa39481d9eb718e872993791547053a3c1f16d5/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp#L150
// https://sourceware.org/git/?p=glibc.git;a=blob;f=nptl/pthread_getattr_np.c;h=de7edfa0928224eb8375e2fe894d6677570fbb3b;hb=HEAD#l188
OverridableBlockSyscallWithErrno(__NR_sched_getaffinity, EPERM);
// https://github.com/llvm/llvm-project/blob/02c2b472b510ff55679844c087b66e7837e13dc2/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp#L434
#ifdef __NR_readlink
OverridableBlockSyscallWithErrno(__NR_readlink, ENOENT);
#endif
OverridableBlockSyscallWithErrno(__NR_readlinkat, ENOENT);
} }
// *san use a custom allocator that runs mmap/unmap under the hood. For
// example:
// https://github.com/llvm/llvm-project/blob/596d534ac3524052df210be8d3c01a33b2260a42/compiler-rt/lib/asan/asan_allocator.cpp#L980
// https://github.com/llvm/llvm-project/blob/62ec4ac90738a5f2d209ed28c822223e58aaaeb7/compiler-rt/lib/sanitizer_common/sanitizer_allocator_secondary.h#L98
AllowMmap();
AllowSyscall(__NR_munmap);
AllowSyscall(__NR_sched_yield);
// https://github.com/llvm/llvm-project/blob/4bbc3290a25c0dc26007912a96e0f77b2092ee56/compiler-rt/lib/sanitizer_common/sanitizer_stack_store.cpp#L293
AddPolicyOnSyscall(__NR_mprotect,
{
ARG_32(2),
BPF_STMT(BPF_AND | BPF_ALU | BPF_K,
~uint32_t{PROT_READ | PROT_WRITE}),
JEQ32(PROT_NONE, ALLOW),
});
AddPolicyOnSyscall(__NR_madvise, {
ARG_32(2),
JEQ32(MADV_DONTDUMP, ALLOW),
JEQ32(MADV_NOHUGEPAGE, ALLOW),
});
// Sanitizers read from /proc. For example:
// https://github.com/llvm/llvm-project/blob/634da7a1c61ee8c173e90a841eb1f4ea03caa20b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp#L1155
AddDirectoryIfNamespaced("/proc");
AllowOpen();
// Sanitizers need pid for reports. For example:
// https://github.com/llvm/llvm-project/blob/634da7a1c61ee8c173e90a841eb1f4ea03caa20b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp#L740
AllowGetPIDs();
// Sanitizers may try color output. For example:
// https://github.com/llvm/llvm-project/blob/87dd3d350c4ce0115b2cdf91d85ddd05ae2661aa/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp#L157
OverridableBlockSyscallWithErrno(__NR_ioctl, EPERM);
// https://github.com/llvm/llvm-project/blob/9aa39481d9eb718e872993791547053a3c1f16d5/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp#L150
// https://sourceware.org/git/?p=glibc.git;a=blob;f=nptl/pthread_getattr_np.c;h=de7edfa0928224eb8375e2fe894d6677570fbb3b;hb=HEAD#l188
OverridableBlockSyscallWithErrno(__NR_sched_getaffinity, EPERM);
// https://github.com/llvm/llvm-project/blob/02c2b472b510ff55679844c087b66e7837e13dc2/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp#L434
#ifdef __NR_readlink
OverridableBlockSyscallWithErrno(__NR_readlink, ENOENT);
#endif
OverridableBlockSyscallWithErrno(__NR_readlinkat, ENOENT);
if constexpr (sapi::sanitizers::IsASan()) { if constexpr (sapi::sanitizers::IsASan()) {
AllowSyscall(__NR_sigaltstack); AllowSyscall(__NR_sigaltstack);
} }
@ -377,6 +380,37 @@ PolicyBuilder& PolicyBuilder::AllowLlvmSanitizers() {
return *this; return *this;
} }
PolicyBuilder& PolicyBuilder::AllowLlvmCoverage() {
if (!sapi::IsCoverageRun()) {
return *this;
}
AllowStat();
AllowGetPIDs();
AllowOpen();
AllowRead();
AllowWrite();
AllowMkdir();
AllowSafeFcntl();
AllowSyscalls({
__NR_munmap, __NR_close, __NR_lseek,
#ifdef __NR__llseek
__NR__llseek, // Newer glibc on PPC
#endif
});
AllowTcMalloc();
AddPolicyOnMmap([](bpf_labels& labels) -> std::vector<sock_filter> {
return {
ARG_32(2), // prot
JNE32(PROT_READ | PROT_WRITE, JUMP(&labels, mmap_end)),
ARG_32(3), // flags
JEQ32(MAP_SHARED, ALLOW),
LABEL(&labels, mmap_end),
};
});
AddDirectoryIfNamespaced(getenv("COVERAGE_DIR"), /*is_ro=*/false);
return *this;
}
PolicyBuilder& PolicyBuilder::AllowLimitedMadvise() { PolicyBuilder& PolicyBuilder::AllowLimitedMadvise() {
return AddPolicyOnSyscall(__NR_madvise, { return AddPolicyOnSyscall(__NR_madvise, {
ARG_32(2), ARG_32(2),

View File

@ -239,6 +239,10 @@ class PolicyBuilder final {
// all binaries. // all binaries.
PolicyBuilder& AllowLlvmSanitizers(); PolicyBuilder& AllowLlvmSanitizers();
// Allows system calls typically used by the LLVM coverage.
// This method is intended as a best effort.
PolicyBuilder& AllowLlvmCoverage();
// Appends code to allow mmap. Specifically this allows mmap and mmap2 syscall // Appends code to allow mmap. Specifically this allows mmap and mmap2 syscall
// on architectures where this syscalls exist. // on architectures where this syscalls exist.
PolicyBuilder& AllowMmap(); PolicyBuilder& AllowMmap();

View File

@ -20,14 +20,11 @@
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "sandboxed_api/config.h" #include "sandboxed_api/config.h"
#include "sandboxed_api/sandbox2/allow_all_syscalls.h" #include "sandboxed_api/sandbox2/allow_all_syscalls.h"
#include "sandboxed_api/sandbox2/policybuilder.h"
#include "sandboxed_api/util/path.h" #include "sandboxed_api/util/path.h"
namespace sapi { namespace sapi {
bool IsCoverageRun() {
return getenv("COVERAGE") != nullptr;
}
sandbox2::PolicyBuilder CreateDefaultPermissiveTestPolicy( sandbox2::PolicyBuilder CreateDefaultPermissiveTestPolicy(
absl::string_view bin_path) { absl::string_view bin_path) {
sandbox2::PolicyBuilder builder; sandbox2::PolicyBuilder builder;
@ -35,6 +32,7 @@ sandbox2::PolicyBuilder CreateDefaultPermissiveTestPolicy(
builder.DefaultAction(sandbox2::AllowAllSyscalls()); builder.DefaultAction(sandbox2::AllowAllSyscalls());
if (sapi::host_os::IsAndroid()) { if (sapi::host_os::IsAndroid()) {
builder.DisableNamespaces(); builder.DisableNamespaces();
return builder;
} }
if (IsCoverageRun()) { if (IsCoverageRun()) {
builder.AddDirectory(getenv("COVERAGE_DIR"), /*is_ro=*/false); builder.AddDirectory(getenv("COVERAGE_DIR"), /*is_ro=*/false);

View File

@ -18,7 +18,7 @@
#include <string> #include <string>
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "sandboxed_api/config.h" #include "sandboxed_api/config.h" // IWYU pragma: export
#include "sandboxed_api/sandbox2/policybuilder.h" #include "sandboxed_api/sandbox2/policybuilder.h"
// The macro SKIP_ANDROID can be used in tests to skip running a // The macro SKIP_ANDROID can be used in tests to skip running a
@ -66,10 +66,14 @@
} \ } \
} while (0) } while (0)
namespace sapi { #define SKIP_SANITIZERS \
do { \
if (sapi::sanitizers::IsAny()) { \
return; \
} \
} while (0)
// Returns whether the executable running under code coverage. namespace sapi {
bool IsCoverageRun();
sandbox2::PolicyBuilder CreateDefaultPermissiveTestPolicy( sandbox2::PolicyBuilder CreateDefaultPermissiveTestPolicy(
absl::string_view bin_path); absl::string_view bin_path);