Skip to content

Commit

Permalink
Sandbox2: Add policy builder helper for POSIX timers
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 670973642
Change-Id: I24e5277443483aac3b558768c6e3c896d0d9b9af
  • Loading branch information
Quincunx271 authored and copybara-github committed Sep 4, 2024
1 parent 9c99d67 commit 96d8728
Show file tree
Hide file tree
Showing 8 changed files with 254 additions and 0 deletions.
1 change: 1 addition & 0 deletions sandboxed_api/sandbox2/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,7 @@ cc_test(
"//sandboxed_api/sandbox2/testcases:minimal",
"//sandboxed_api/sandbox2/testcases:minimal_dynamic",
"//sandboxed_api/sandbox2/testcases:policy",
"//sandboxed_api/sandbox2/testcases:posix_timers",
],
tags = ["no_qemu_user_mode"],
deps = [
Expand Down
1 change: 1 addition & 0 deletions sandboxed_api/sandbox2/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,7 @@ if(BUILD_TESTING AND SAPI_BUILD_TESTING)
sandbox2::testcase_minimal
sandbox2::testcase_minimal_dynamic
sandbox2::testcase_policy
sandbox2::testcase_posix_timers
)
target_link_libraries(sandbox2_policy_test PRIVATE
absl::strings
Expand Down
73 changes: 73 additions & 0 deletions sandboxed_api/sandbox2/policy_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,79 @@ TEST(PolicyTest, IsattyAllowed) {
ASSERT_THAT(result.final_status(), Eq(Result::OK));
}

PolicyBuilder PosixTimersPolicyBuilder(absl::string_view path) {
return PolicyBuilder()
// Required by google infra / logging.
.AllowDynamicStartup()
.AllowWrite()
.AllowSyscall(__NR_getcwd)
.AllowMmap()
.AllowMlock()
.AllowMkdir()
.AllowGetIDs()
.AllowExit()
.AllowRestartableSequences(PolicyBuilder::kAllowSlowFences)
.AllowSyscall(__NR_rt_sigtimedwait)
// Features used by the binary.
.AllowHandleSignals()
.AllowGetPIDs()
.AllowTime()
.AllowSleep()
.AllowAlarm()
// Posix timers themselves.
.AllowPosixTimers();
}

TEST(PolicyTest, PosixTimersWorkIfAllowed) {
SKIP_SANITIZERS;
const std::string path = GetTestSourcePath("sandbox2/testcases/posix_timers");
for (absl::string_view kind : {"SIGEV_NONE", "SIGEV_SIGNAL",
"SIGEV_THREAD_ID", "syscall(SIGEV_THREAD)"}) {
std::vector<std::string> args = {path, "--sigev_notify_kind",
std::string(kind)};

SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
PosixTimersPolicyBuilder(path).TryBuild());
auto executor = std::make_unique<Executor>(path, args);
Sandbox2 sandbox(std::move(executor), std::move(policy));
Result result = sandbox.Run();
EXPECT_EQ(result.final_status(), Result::OK) << kind;
}
}

TEST(PolicyTest, PosixTimersCannotCreateThreadsIfThreadsAreProhibited) {
SKIP_SANITIZERS;
const std::string path = GetTestSourcePath("sandbox2/testcases/posix_timers");
std::vector<std::string> args = {
path,
// SIGEV_THREAD creates a thread as an implementation detail.
"--sigev_notify_kind=SIGEV_THREAD",
};

SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
PosixTimersPolicyBuilder(path).TryBuild());
auto executor = std::make_unique<Executor>(path, args);
Sandbox2 sandbox(std::move(executor), std::move(policy));
Result result = sandbox.Run();
EXPECT_EQ(result.final_status(), Result::VIOLATION);
}

TEST(PolicyTest, PosixTimersCanCreateThreadsIfThreadsAreAllowed) {
SKIP_SANITIZERS;
const std::string path = GetTestSourcePath("sandbox2/testcases/posix_timers");
std::vector<std::string> args = {path, "--sigev_notify_kind=SIGEV_THREAD"};

SAPI_ASSERT_OK_AND_ASSIGN(auto policy, PosixTimersPolicyBuilder(path)
.AllowFork()
// For Arm.
.AllowSyscall(__NR_madvise)
.TryBuild());
auto executor = std::make_unique<Executor>(path, args);
Sandbox2 sandbox(std::move(executor), std::move(policy));
Result result = sandbox.Run();
EXPECT_EQ(result.final_status(), Result::OK);
}

std::unique_ptr<Policy> MinimalTestcasePolicy(absl::string_view path = "") {
PolicyBuilder builder;

Expand Down
10 changes: 10 additions & 0 deletions sandboxed_api/sandbox2/policybuilder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,16 @@ PolicyBuilder& PolicyBuilder::AllowAlarm() {
__NR_setitimer});
}

PolicyBuilder& PolicyBuilder::AllowPosixTimers() {
return AllowSyscalls({
__NR_timer_create,
__NR_timer_delete,
__NR_timer_settime,
__NR_timer_gettime,
__NR_timer_getoverrun,
});
}

PolicyBuilder& PolicyBuilder::AllowHandleSignals() {
return AllowSyscalls({
__NR_restart_syscall,
Expand Down
9 changes: 9 additions & 0 deletions sandboxed_api/sandbox2/policybuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,15 @@ class PolicyBuilder final {
// - setitimer
PolicyBuilder& AllowAlarm();

// Appends code to allow setting posix timers.
// Allows these syscalls:
// - timer_create
// - timer_delete
// - timer_settime
// - timer_gettime
// - timer_getoverrun
PolicyBuilder& AllowPosixTimers();

// Appends code to allow setting up signal handlers, returning from them, etc.
// Allows these syscalls:
// - rt_sigaction
Expand Down
20 changes: 20 additions & 0 deletions sandboxed_api/sandbox2/testcases/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -281,3 +281,23 @@ cc_binary(
srcs = ["util_communicate.cc"],
copts = sapi_platform_copts(),
)

cc_binary(
name = "posix_timers",
testonly = True,
srcs = ["posix_timers.cc"],
copts = sapi_platform_copts(),
features = ["fully_static_link"],
linkopts = ["-lrt"],
deps = [
"@com_google_absl//absl/base:log_severity",
"@com_google_absl//absl/flags:flag",
"@com_google_absl//absl/flags:parse",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/log:globals",
"@com_google_absl//absl/log:initialize",
"@com_google_absl//absl/strings:string_view",
"@com_google_absl//absl/time",
],
)
23 changes: 23 additions & 0 deletions sandboxed_api/sandbox2/testcases/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,26 @@ set_target_properties(sandbox2_testcase_util_communicate PROPERTIES
target_link_libraries(sandbox2_testcase_util_communicate PRIVATE
sapi::base
)

# sandboxed_api/sandbox2/testcases:posix_timers
add_executable(sandbox2_testcase_posix_timers
posix_timers.cc
)
add_executable(sandbox2::testcase_posix_timers ALIAS sandbox2_testcase_posix_timers)
set_target_properties(sandbox2_testcase_posix_timers PROPERTIES
OUTPUT_NAME posix_timers
)
target_link_libraries(sandbox2_testcase_posix_timers PRIVATE
-static
-lrt
absl::check
absl::flags
absl::flags_parse
absl::log
absl::log_globals
absl::log_initialize
absl::log_severity
absl::strings
absl::time
sapi::base
)
117 changes: 117 additions & 0 deletions sandboxed_api/sandbox2/testcases/posix_timers.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#include <sys/syscall.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>

#include <atomic>
#include <csignal>
#include <string>

#include "absl/base/log_severity.h"
#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "absl/log/check.h"
#include "absl/log/globals.h"
#include "absl/log/initialize.h"
#include "absl/log/log.h"
#include "absl/strings/string_view.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"

ABSL_FLAG(std::string, sigev_notify_kind, "",
"The C name for the kind of POSIX timer to create (sigev_notify), or "
"\"syscall(SIGEV_THREAD)\" for a manual syscall approach which "
"checks that no threads were created.");

int main(int argc, char* argv[]) {
absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo);
absl::ParseCommandLine(argc, argv);
absl::InitializeLog();

static std::atomic<bool> timer_expired(false);
static std::atomic<pid_t> tid(0);
static_assert(std::atomic<pid_t>::is_always_lock_free);
// Handle SIGPROF by recording that it arrived.
signal(
SIGPROF, +[](int) {
timer_expired.store(true);
tid.store(syscall(__NR_gettid));
});

const std::string sigev_notify_kind = absl::GetFlag(FLAGS_sigev_notify_kind);
struct sigevent sev {};
sev.sigev_signo = SIGPROF;

if (sigev_notify_kind == "SIGEV_THREAD" ||
sigev_notify_kind == "syscall(SIGEV_THREAD)") {
sev.sigev_notify = SIGEV_THREAD;
sev.sigev_notify_function = +[](sigval_t) {
timer_expired.store(true);
tid.store(syscall(__NR_gettid));
};
} else if (sigev_notify_kind == "SIGEV_SIGNAL") {
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGPROF;
} else if (sigev_notify_kind == "SIGEV_NONE") {
sev.sigev_notify = SIGEV_NONE;
} else if (sigev_notify_kind == "SIGEV_THREAD_ID") {
sev.sigev_notify = SIGEV_THREAD_ID;
sev.sigev_signo = SIGPROF;
#ifndef sigev_notify_thread_id
sev._sigev_un._tid = syscall(__NR_gettid);
#else
sev.sigev_notify_thread_id = syscall(__NR_gettid);
#endif
} else {
LOG(QFATAL) << "Invalid --sigev_notify_kind: " << sigev_notify_kind;
}

struct itimerspec timerspec {};
timerspec.it_interval.tv_sec = 0;
timerspec.it_interval.tv_nsec = 1'000'000;
timerspec.it_value.tv_sec = 0;
timerspec.it_value.tv_nsec = 1'000'000;

if (sigev_notify_kind == "syscall(SIGEV_THREAD)") {
// Use raw syscalls.
int timer;
PCHECK(syscall(__NR_timer_create, CLOCK_REALTIME, &sev, &timer) == 0);
PCHECK(syscall(__NR_timer_settime, timer, 0, &timerspec, nullptr) == 0);

// Long enough to effectively guarantee that we see the notification.
absl::SleepFor(absl::Milliseconds(30));

PCHECK(syscall(__NR_timer_gettime, timer, &timerspec) == 0);
PCHECK(syscall(__NR_timer_getoverrun, timer) != -1);

PCHECK(syscall(__NR_timer_delete, timer) == 0);

// The syscall with SIGEV_THREAD doesn't spawn a thread, which we can verify
// by checking that the thread ID is the main thread.
CHECK_EQ(tid.load(), syscall(__NR_gettid));
} else {
timer_t timer;
PCHECK(timer_create(CLOCK_REALTIME, &sev, &timer) == 0);
PCHECK(timer_settime(timer, 0, &timerspec, nullptr) == 0);

// Long enough to effectively guarantee that we see the notification.
absl::SleepFor(absl::Milliseconds(30));

PCHECK(timer_gettime(timer, &timerspec) == 0);
PCHECK(timer_getoverrun(timer) != -1);

PCHECK(timer_delete(timer) == 0);
}

if (sigev_notify_kind == "SIGEV_THREAD" ||
sigev_notify_kind == "syscall(SIGEV_THREAD)" ||
sigev_notify_kind == "SIGEV_THREAD_ID" ||
sigev_notify_kind == "SIGEV_SIGNAL") {
CHECK(timer_expired.load());
} else {
CHECK_EQ(sigev_notify_kind, "SIGEV_NONE");
CHECK(!timer_expired.load());
}

return 0;
}

0 comments on commit 96d8728

Please sign in to comment.