blob: 998cd3bd51d22964120ff17f477c57561b760b48 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Sanitizers internally use some syscalls which non-SFI NaCl disallows.
#if !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER) && \
!defined(MEMORY_SANITIZER) && !defined(LEAK_SANITIZER)
#include "components/nacl/loader/nonsfi/nonsfi_sandbox.h"
#include <errno.h>
#include <fcntl.h>
#include <linux/net.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <memory>
#include "base/at_exit.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/posix/eintr_wrapper.h"
#include "base/sys_info.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
#include "sandbox/linux/seccomp-bpf/syscall.h"
#include "sandbox/linux/system_headers/linux_futex.h"
#include "sandbox/linux/system_headers/linux_signal.h"
#include "sandbox/linux/system_headers/linux_syscalls.h"
// These defines are for PNaCl toolchain build.
#if !defined(F_DUPFD_CLOEXEC)
#define F_DUPFD_CLOEXEC 1030
#endif
#if !defined(MAP_POPULATE)
#define MAP_POPULATE 0x8000
#endif
#if !defined(PROT_GROWSDOWN)
#define PROT_GROWSDOWN 0x01000000
#endif
#if !defined(CLOCK_MONOTONIC_RAW)
#define CLOCK_MONOTONIC_RAW 4
#endif
#if !defined(AF_INET)
#define AF_INET 2
#endif
#if defined(__i386__)
#if !defined(SYS_SOCKET)
#define SYS_SOCKET 1
#endif
#if !defined(SYS_BIND)
#define SYS_BIND 2
#endif
#if !defined(SYS_CONNECT)
#define SYS_CONNECT 3
#endif
#if !defined(SYS_LISTEN)
#define SYS_LISTEN 4
#endif
#if !defined(SYS_ACCEPT)
#define SYS_ACCEPT 5
#endif
#if !defined(SYS_GETSOCKNAME)
#define SYS_GETSOCKNAME 6
#endif
#if !defined(SYS_GETPEERNAME)
#define SYS_GETPEERNAME 7
#endif
#if !defined(SYS_SETSOCKOPT)
#define SYS_SETSOCKOPT 14
#endif
#if !defined(SYS_GETSOCKOPT)
#define SYS_GETSOCKOPT 15
#endif
#endif // defined(__i386__)
namespace {
void DoPipe(base::ScopedFD* fds) {
int tmp_fds[2];
BPF_ASSERT_EQ(0, pipe(tmp_fds));
fds[0].reset(tmp_fds[0]);
fds[1].reset(tmp_fds[1]);
}
TEST(NaClNonSfiSandboxTest, BPFIsSupported) {
bool seccomp_bpf_supported = sandbox::SandboxBPF::SupportsSeccompSandbox(
sandbox::SandboxBPF::SeccompLevel::SINGLE_THREADED);
if (!seccomp_bpf_supported) {
LOG(ERROR) << "Seccomp BPF is not supported, these tests "
<< "will pass without running";
}
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
invalid_sysno,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
syscall(999);
}
const int kExpectedValue = 123;
void* SetValueInThread(void* test_val_ptr) {
*reinterpret_cast<int*>(test_val_ptr) = kExpectedValue;
return NULL;
}
// To make this test pass, we need to allow sched_getaffinity and
// mmap. We just disable this test not to complicate the sandbox.
BPF_TEST_C(NaClNonSfiSandboxTest,
clone_by_pthread_create,
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
// clone call for thread creation is allowed.
pthread_t th;
int test_val = 42;
BPF_ASSERT_EQ(0, pthread_create(&th, NULL, &SetValueInThread, &test_val));
BPF_ASSERT_EQ(0, pthread_join(th, NULL));
BPF_ASSERT_EQ(kExpectedValue, test_val);
}
int DoFork() {
// Call clone() to do a fork().
const int pid = syscall(__NR_clone, LINUX_SIGCHLD, NULL);
if (pid == 0)
_exit(0);
return pid;
}
// The sanity check for DoFork without the sandbox.
TEST(NaClNonSfiSandboxTest, DoFork) {
const int pid = DoFork();
ASSERT_LT(0, pid);
int status;
ASSERT_EQ(pid, HANDLE_EINTR(waitpid(pid, &status, 0)));
ASSERT_TRUE(WIFEXITED(status));
ASSERT_EQ(0, WEXITSTATUS(status));
}
// Then, try this in the sandbox.
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
clone_for_fork,
DEATH_SEGV_MESSAGE(
sandbox::GetCloneErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
DoFork();
}
BPF_TEST_C(NaClNonSfiSandboxTest,
prctl_SET_NAME,
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
errno = 0;
BPF_ASSERT_EQ(-1, syscall(__NR_prctl, PR_SET_NAME, "foo"));
BPF_ASSERT_EQ(EPERM, errno);
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
prctl_SET_DUMPABLE,
DEATH_SEGV_MESSAGE(
sandbox::GetPrctlErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
syscall(__NR_prctl, PR_SET_DUMPABLE, 1UL);
}
BPF_DEATH_TEST_C(NaClNonsfiSandboxTest,
socketpair_af_unix_disallowed,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
int tmp_fds[2];
socketpair(AF_UNIX, SOCK_STREAM, 0, tmp_fds);
}
// On arm and x86_64 the arguments to socketpair are passed in registers,
// so they can be filtered by seccomp-bpf. This filter cannot be applied
// on x86_32 as the arguments are passed in memory.
#if defined(__x86_64__) || defined(__arm__)
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
socketpair_af_inet_disallowed,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
int fds[2];
socketpair(AF_INET, SOCK_STREAM, 0, fds);
}
#endif
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
accept,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
#if defined(__i386__)
uintptr_t args[] = {0, 0, 0};
syscall(__NR_socketcall, SYS_ACCEPT, args);
#else
syscall(__NR_accept, 0, 0, 0);
#endif
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
bind,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
#if defined(__i386__)
uintptr_t args[] = {0, 0, 0};
syscall(__NR_socketcall, SYS_BIND, args);
#else
syscall(__NR_bind, 0, 0, 0);
#endif
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
connect,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
#if defined(__i386__)
uintptr_t args[] = {0, 0, 0};
syscall(__NR_socketcall, SYS_CONNECT, args);
#else
syscall(__NR_connect, 0, 0, 0);
#endif
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
getpeername,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
#if defined(__i386__)
uintptr_t args[] = {0, 0, 0};
syscall(__NR_socketcall, SYS_GETPEERNAME, args);
#else
syscall(__NR_getpeername, 0, 0, 0);
#endif
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
getsockname,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
#if defined(__i386__)
uintptr_t args[] = {0, 0, 0};
syscall(__NR_socketcall, SYS_GETSOCKNAME, args);
#else
syscall(__NR_getsockname, 0, 0, 0);
#endif
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
getsockopt,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
#if defined(__i386__)
uintptr_t args[] = {0, 0, 0, 0, 0};
syscall(__NR_socketcall, SYS_GETSOCKOPT, args);
#else
syscall(__NR_getsockname, 0, 0, 0, 0, 0);
#endif
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
listen,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
#if defined(__i386__)
uintptr_t args[] = {0, 0};
syscall(__NR_socketcall, SYS_LISTEN, args);
#else
syscall(__NR_listen, 0, 0);
#endif
}
// On x86_64 architecture, there is no __NR_recv system call. Note: recv()
// syscall wrapper usually uses __NR_recvfrom, instead, (like in glibc).
#if defined(__i386__) || defined(__arm__)
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
recv,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
#if defined(__i386__)
uintptr_t args[] = {0, 0, 0, 0};
syscall(__NR_socketcall, SYS_RECV, args);
#else
syscall(__NR_recv, 0, 0, 0, 0);
#endif
}
#endif
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
recvfrom,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
#if defined(__i386__)
uintptr_t args[] = {0, 0, 0, 0, 0, 0};
syscall(__NR_socketcall, SYS_RECVFROM, args);
#else
syscall(__NR_recvfrom, 0, 0, 0, 0, 0, 0);
#endif
}
// On x86_64 architecture, there is no __NR_send system call. Note: send()
// syscall wrapper usually uses __NR_sendto, instead, (like in glibc).
#if defined(__i386__) || defined(__arm__)
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
send,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
#if defined(__i386__)
uintptr_t args[] = {0, 0, 0, 0};
syscall(__NR_socketcall, SYS_SEND, args);
#else
syscall(__NR_send, 0, 0, 0, 0);
#endif
}
#endif
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
sendto,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
#if defined(__i386__)
uintptr_t args[] = {0, 0, 0, 0, 0, 0};
syscall(__NR_socketcall, SYS_SENDTO, args);
#else
syscall(__NR_sendto, 0, 0, 0, 0, 0, 0);
#endif
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
setsockopt,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
#if defined(__i386__)
uintptr_t args[] = {0, 0, 0, 0, 0};
syscall(__NR_socketcall, SYS_SETSOCKOPT, args);
#else
syscall(__NR_setsockopt, 0, 0, 0, 0, 0);
#endif
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
socket,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
#if defined(__i386__)
uintptr_t args[] = {0, 0, 0};
syscall(__NR_socketcall, SYS_SOCKET, args);
#else
syscall(__NR_socket, 0, 0, 0);
#endif
}
BPF_TEST_C(NaClNonSfiSandboxTest,
fcntl_SETFD_allowed,
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
base::ScopedFD fds[2];
DoPipe(fds);
BPF_ASSERT_EQ(0, fcntl(fds[0].get(), F_SETFD, FD_CLOEXEC));
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
fcntl_SETFD,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
base::ScopedFD fds[2];
DoPipe(fds);
fcntl(fds[0].get(), F_SETFD, 99);
}
BPF_TEST_C(NaClNonSfiSandboxTest,
fcntl_GETFL_SETFL_allowed,
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
base::ScopedFD fds[2];
DoPipe(fds);
const int fd = fds[0].get();
BPF_ASSERT_EQ(0, fcntl(fd, F_GETFL));
BPF_ASSERT_EQ(0, fcntl(fd, F_SETFL, O_RDWR | O_NONBLOCK));
BPF_ASSERT_EQ(O_NONBLOCK, fcntl(fd, F_GETFL));
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
fcntl_GETFL_SETFL,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
base::ScopedFD fds[2];
DoPipe(fds);
fcntl(fds[0].get(), F_SETFL, O_APPEND);
}
void DoFcntl(int fd, int cmd) {
// fcntl in PNaCl toolchain returns an error without calling actual system
// call for unknown |cmd|. So, instead, here we use syscall().
syscall(__NR_fcntl64, fd, cmd);
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
fcntl_DUPFD,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
DoFcntl(0, F_DUPFD);
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
fcntl_DUPFD_CLOEXEC,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
DoFcntl(0, F_DUPFD_CLOEXEC);
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
FutexWithRequeuePriorityInheritence,
DEATH_SEGV_MESSAGE(
sandbox::GetFutexErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
syscall(__NR_futex, NULL, FUTEX_CMP_REQUEUE_PI, 0, NULL, NULL, 0);
_exit(1);
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
FutexWithRequeuePriorityInheritencePrivate,
DEATH_SEGV_MESSAGE(
sandbox::GetFutexErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
syscall(__NR_futex, NULL, FUTEX_CMP_REQUEUE_PI_PRIVATE, 0, NULL, NULL, 0);
_exit(1);
}
BPF_TEST_C(NaClNonSfiSandboxTest,
StartingAndJoiningThreadWorks,
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
base::Thread thread("sandbox_tests");
BPF_ASSERT(thread.Start());
// |thread|'s destructor will join the thread.
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
FutexWithUnlockPIPrivate,
DEATH_SEGV_MESSAGE(
sandbox::GetFutexErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
syscall(__NR_futex, NULL, FUTEX_UNLOCK_PI_PRIVATE, 0, NULL, NULL, 0);
_exit(1);
}
void* DoMmap(int prot, int flags) {
// When PROT_EXEC is set, PNaCl toolchain's mmap() system call wrapper uses
// two system calls mmap2(2) and mprotect(2), so that we cannot test
// sandbox with the wrapper. Instead, here we use syscall().
return reinterpret_cast<void*>(
syscall(__NR_mmap2, NULL, getpagesize(), prot, flags, -1, 0));
}
void* DoAllowedAnonymousMmap() {
return DoMmap(PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED);
}
BPF_TEST_C(NaClNonSfiSandboxTest,
mmap_allowed,
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
void* ptr = DoAllowedAnonymousMmap();
BPF_ASSERT_NE(MAP_FAILED, ptr);
BPF_ASSERT_EQ(0, munmap(ptr, getpagesize()));
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
mmap_unallowed_flag,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
DoMmap(PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_POPULATE);
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
mmap_unallowed_prot,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
DoMmap(PROT_READ | PROT_GROWSDOWN, MAP_ANONYMOUS);
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
mmap_exec,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
DoMmap(PROT_EXEC, MAP_ANONYMOUS);
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
mmap_read_exec,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
DoMmap(PROT_READ | PROT_EXEC, MAP_ANONYMOUS);
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
mmap_write_exec,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
DoMmap(PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS);
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
mmap_read_write_exec,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
DoMmap(PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS);
}
BPF_TEST_C(NaClNonSfiSandboxTest,
mprotect_allowed,
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
void* ptr = DoAllowedAnonymousMmap();
BPF_ASSERT_NE(MAP_FAILED, ptr);
BPF_ASSERT_EQ(0, mprotect(ptr, getpagesize(), PROT_READ));
BPF_ASSERT_EQ(0, munmap(ptr, getpagesize()));
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
mprotect_unallowed_prot,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
// We have tested DoAllowedAnonymousMmap is allowed in
// mmap_allowed, so we can make sure the following mprotect call
// kills the process.
void* ptr = DoAllowedAnonymousMmap();
BPF_ASSERT_NE(MAP_FAILED, ptr);
mprotect(ptr, getpagesize(), PROT_READ | PROT_GROWSDOWN);
}
BPF_TEST_C(NaClNonSfiSandboxTest,
brk,
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
char* next_brk = static_cast<char*>(sbrk(0)) + getpagesize();
// The kernel interface must return zero for brk.
BPF_ASSERT_EQ(0, syscall(__NR_brk, next_brk));
}
// clockid restrictions are mostly tested in sandbox/ with the
// RestrictClockID() unittests. Some basic tests are duplicated here as
// a precaution.
void CheckClock(clockid_t clockid) {
struct timespec ts;
ts.tv_sec = ts.tv_nsec = -1;
BPF_ASSERT_EQ(0, clock_gettime(clockid, &ts));
BPF_ASSERT_LE(0, ts.tv_sec);
BPF_ASSERT_LE(0, ts.tv_nsec);
}
BPF_TEST_C(NaClNonSfiSandboxTest,
clock_gettime_allowed,
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
CheckClock(CLOCK_MONOTONIC);
CheckClock(CLOCK_PROCESS_CPUTIME_ID);
CheckClock(CLOCK_REALTIME);
CheckClock(CLOCK_THREAD_CPUTIME_ID);
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
clock_gettime_crash_monotonic_raw,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
}
BPF_DEATH_TEST_C(NaClNonSfiSandboxTest,
invalid_syscall_crash,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) {
sandbox::Syscall::InvalidCall();
}
// The following tests check for several restrictions in tgkill(). A delegate is
// needed to be able to call getpid() from inside the process that will be
// sandboxed, but before the sandbox is installed.
template<void(*callback)(int pid, int tid)>
class TgkillDelegate : public sandbox::BPFTesterDelegate {
public:
TgkillDelegate() {}
~TgkillDelegate() override {}
std::unique_ptr<sandbox::bpf_dsl::Policy> GetSandboxBPFPolicy() override {
// These two values must be obtained when running in the sandboxed process.
// They cannot be set in the constructor and are also not available from
// within |RunTestFunction|.
pid_ = getpid();
tid_ = syscall(__NR_gettid);
return std::unique_ptr<sandbox::bpf_dsl::Policy>(
new nacl::nonsfi::NaClNonSfiBPFSandboxPolicy());
}
void RunTestFunction() override {
callback(pid_, tid_);
}
// These are longs as a temporary workaround for crbug.com/532992.
long pid_;
long tid_;
private:
DISALLOW_COPY_AND_ASSIGN(TgkillDelegate);
};
void BPF_TEST_D_tgkill_with_invalid_signal(int pid, int tid) {
syscall(__NR_tgkill, pid, tid, SIGKILL);
}
BPF_DEATH_TEST_D(NaClNonSfiSandboxTest,
tgkill_with_invalid_signal,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
TgkillDelegate<BPF_TEST_D_tgkill_with_invalid_signal>);
void BPF_TEST_D_tgkill_with_invalid_tgid(int pid, int tid) {
syscall(__NR_tgkill, 1, tid, LINUX_SIGUSR1);
}
BPF_DEATH_TEST_D(NaClNonSfiSandboxTest,
tgkill_with_invalid_tgid,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
TgkillDelegate<BPF_TEST_D_tgkill_with_invalid_tgid>);
void BPF_TEST_D_tgkill_with_negative_tgid(int pid, int tid) {
syscall(__NR_tgkill, pid, -1, LINUX_SIGUSR1);
}
BPF_DEATH_TEST_D(NaClNonSfiSandboxTest,
tgkill_with_negative_tgid,
DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
TgkillDelegate<BPF_TEST_D_tgkill_with_negative_tgid>);
void BPF_TEST_D_tgkill_with_invalid_tid(int pid, int tid) {
BPF_ASSERT_EQ(-1, syscall(__NR_tgkill, pid, 1, LINUX_SIGUSR1));
BPF_ASSERT_EQ(ESRCH, errno);
}
BPF_TEST_D(NaClNonSfiSandboxTest,
tgkill_with_invalid_tid,
TgkillDelegate<BPF_TEST_D_tgkill_with_invalid_tid>);
// The following test cases check if syscalls return EPERM regardless
// of arguments.
#define RESTRICT_SYSCALL_EPERM_TEST(name) \
BPF_TEST_C(NaClNonSfiSandboxTest, \
name##_EPERM, \
nacl::nonsfi::NaClNonSfiBPFSandboxPolicy) { \
errno = 0; \
BPF_ASSERT_EQ(-1, syscall(__NR_##name, 0, 0, 0, 0, 0, 0)); \
BPF_ASSERT_EQ(EPERM, errno); \
}
RESTRICT_SYSCALL_EPERM_TEST(epoll_create);
#if defined(__i386__) || defined(__arm__)
RESTRICT_SYSCALL_EPERM_TEST(getegid32);
RESTRICT_SYSCALL_EPERM_TEST(geteuid32);
RESTRICT_SYSCALL_EPERM_TEST(getgid32);
RESTRICT_SYSCALL_EPERM_TEST(getuid32);
#endif
RESTRICT_SYSCALL_EPERM_TEST(getegid);
RESTRICT_SYSCALL_EPERM_TEST(geteuid);
RESTRICT_SYSCALL_EPERM_TEST(getgid);
RESTRICT_SYSCALL_EPERM_TEST(getuid);
RESTRICT_SYSCALL_EPERM_TEST(madvise);
RESTRICT_SYSCALL_EPERM_TEST(open);
RESTRICT_SYSCALL_EPERM_TEST(openat);
RESTRICT_SYSCALL_EPERM_TEST(ptrace);
RESTRICT_SYSCALL_EPERM_TEST(set_robust_list);
#if defined(__i386__) || defined(__x86_64__)
RESTRICT_SYSCALL_EPERM_TEST(time);
#endif
} // namespace
#endif // !ADDRESS_SANITIZER && !THREAD_SANITIZER &&
// !MEMORY_SANITIZER && !LEAK_SANITIZER