sandboxed-api/sandboxed_api/sandbox2/regs_test.cc

103 lines
3.2 KiB
C++

#include "sandboxed_api/sandbox2/regs.h"
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <sys/prctl.h>
#include <sys/ptrace.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <syscall.h>
#include <unistd.h>
#include <cerrno>
#include <cstdint>
#include <cstdlib>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/log/check.h"
#include "sandboxed_api/config.h"
#include "sandboxed_api/sandbox2/sanitizer.h"
#include "sandboxed_api/sandbox2/syscall.h"
#include "sandboxed_api/sandbox2/util.h"
#include "sandboxed_api/sandbox2/util/bpf_helper.h"
#include "sandboxed_api/util/status_matchers.h"
namespace sandbox2 {
namespace {
using ::sapi::IsOk;
#define __WPTRACEEVENT(x) ((x & 0xff0000) >> 16)
TEST(RegsTest, SkipSyscallWorks) {
std::vector<sock_filter> policy = {
LOAD_SYSCALL_NR,
JEQ32(__NR_getpid, TRACE(0)),
ALLOW,
};
sock_fprog prog = {
.len = static_cast<uint8_t>(policy.size()),
.filter = policy.data(),
};
// Create socketpair for synchronization
int sv[2];
ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, sv), 0);
// Fork a child process to run the syscalls in.
pid_t ppid = util::Syscall(__NR_gettid);
pid_t pid = fork();
ASSERT_NE(pid, -1);
char c = 'C';
if (pid == 0) {
// Get ready for being ptraced.
sanitizer::WaitForSanitizer();
CHECK_EQ(prctl(PR_SET_DUMPABLE, 1), 0);
prctl(PR_SET_PTRACER, ppid);
CHECK_EQ(prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0), 0);
CHECK_EQ(prctl(PR_SET_KEEPCAPS, 0), 0);
// Notify parent that we're ready for ptrace.
CHECK_EQ(write(sv[0], &c, 1), 1);
// Apply seccomp policy
CHECK_EQ(util::Syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, 0,
reinterpret_cast<uintptr_t>(&prog)),
0);
// Wait for tracer to be attached.
CHECK_EQ(read(sv[0], &c, 1), 1);
// Run the test syscall
errno = 0;
util::Syscall(__NR_getpid, 123, reinterpret_cast<uintptr_t>(&c), 1);
_Exit(errno == ENOENT ? 0 : 1);
}
// Wait for child to be ready for ptrace
ASSERT_EQ(read(sv[1], &c, 1), 1);
ASSERT_EQ(ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_TRACESECCOMP), 0);
// Notify child it has been ptraced.
ASSERT_EQ(write(sv[1], &c, 1), 1);
// Wait for seccomp TRACE stop
int status;
ASSERT_EQ(waitpid(pid, &status, __WNOTHREAD | __WALL | WUNTRACED), pid);
ASSERT_TRUE(WIFSTOPPED(status));
ASSERT_EQ(__WPTRACEEVENT(status), PTRACE_EVENT_SECCOMP);
// Fetch the registers
Regs regs(pid);
ASSERT_THAT(regs.Fetch(), IsOk());
// Check syscall arguments
Syscall syscall = regs.ToSyscall(sapi::host_cpu::Architecture());
EXPECT_EQ(syscall.nr(), __NR_getpid);
EXPECT_EQ(syscall.args()[0], 123);
EXPECT_EQ(syscall.args()[1], reinterpret_cast<uintptr_t>(&c));
EXPECT_EQ(syscall.args()[2], 1);
// Skip syscall
ASSERT_THAT(regs.SkipSyscallReturnValue(-ENOENT), IsOk());
// Continue&detach the child process
ASSERT_EQ(ptrace(PTRACE_DETACH, pid, 0, 0), 0);
// Wait for the child to exit
ASSERT_EQ(waitpid(pid, &status, __WNOTHREAD | __WALL | WUNTRACED), pid);
ASSERT_TRUE(WIFEXITED(status));
EXPECT_EQ(WEXITSTATUS(status), 0);
}
} // namespace
} // namespace sandbox2