Path: blob/main/contrib/capsicum-test/capsicum-test.h
39475 views
/* -*- C++ -*- */1#ifndef CAPSICUM_TEST_H2#define CAPSICUM_TEST_H34#include <errno.h>5#include <sys/types.h>6#include <sys/wait.h>7#include <sys/resource.h>8#include <signal.h>910#include <ios>11#include <ostream>12#include <string>1314#include "gtest/gtest.h"1516extern bool verbose;17extern std::string tmpdir;18extern bool tmpdir_on_tmpfs;19extern bool force_mt;20extern bool force_nofork;21extern uid_t other_uid;2223static inline void *WaitingThreadFn(void *) {24// Loop until cancelled25while (true) {26usleep(10000);27pthread_testcancel();28}29return NULL;30}3132// If force_mt is set, run another thread in parallel with the test. This forces33// the kernel into multi-threaded mode.34template <typename T, typename Function>35void MaybeRunWithThread(T *self, Function fn) {36pthread_t subthread;37if (force_mt) {38pthread_create(&subthread, NULL, WaitingThreadFn, NULL);39}40(self->*fn)();41if (force_mt) {42pthread_cancel(subthread);43pthread_join(subthread, NULL);44}45}46template <typename Function>47void MaybeRunWithThread(Function fn) {48pthread_t subthread;49if (force_mt) {50pthread_create(&subthread, NULL, WaitingThreadFn, NULL);51}52(fn)();53if (force_mt) {54pthread_cancel(subthread);55pthread_join(subthread, NULL);56}57}5859// Return the absolute path of a filename in the temp directory, `tmpdir`,60// with the given pathname, e.g., "/tmp/<pathname>", if `tmpdir` was set to61// "/tmp".62const char *TmpFile(const char *pathname);6364// Run the given test function in a forked process, so that trapdoor65// entry doesn't affect other tests, and watch out for hung processes.66// Implemented as a macro to allow access to the test case instance's67// HasFailure() method, which is reported as the forked process's68// exit status.69#define _RUN_FORKED(INNERCODE, TESTCASENAME, TESTNAME) \70pid_t pid = force_nofork ? 0 : fork(); \71if (pid == 0) { \72INNERCODE; \73if (!force_nofork) { \74exit(HasFailure()); \75} \76} else if (pid > 0) { \77int rc, status; \78int remaining_us = 30000000; \79while (remaining_us > 0) { \80status = 0; \81rc = waitpid(pid, &status, WNOHANG); \82if (rc != 0) break; \83remaining_us -= 10000; \84usleep(10000); \85} \86if (remaining_us <= 0) { \87fprintf(stderr, "Warning: killing unresponsive test " \88"%s.%s (pid %d)\n", \89TESTCASENAME, TESTNAME, pid); \90kill(pid, SIGKILL); \91ADD_FAILURE() << "Test hung"; \92} else if (rc < 0) { \93fprintf(stderr, "Warning: waitpid error %s (%d)\n", \94strerror(errno), errno); \95ADD_FAILURE() << "Failed to wait for child"; \96} else { \97int rc = WIFEXITED(status) ? WEXITSTATUS(status) : -1; \98EXPECT_EQ(0, rc); \99} \100}101#define _RUN_FORKED_MEM(THIS, TESTFN, TESTCASENAME, TESTNAME) \102_RUN_FORKED(MaybeRunWithThread(THIS, &TESTFN), TESTCASENAME, TESTNAME);103#define _RUN_FORKED_FN(TESTFN, TESTCASENAME, TESTNAME) \104_RUN_FORKED(MaybeRunWithThread(&TESTFN), TESTCASENAME, TESTNAME);105106// Run a test case in a forked process, possibly cleaning up a107// test file after completion108#define FORK_TEST_ON(test_case_name, test_name, test_file) \109static void test_case_name##_##test_name##_ForkTest(); \110TEST(test_case_name, test_name ## Forked) { \111_RUN_FORKED_FN(test_case_name##_##test_name##_ForkTest, \112#test_case_name, #test_name); \113const char *filename = test_file; \114if (filename) unlink(filename); \115} \116static void test_case_name##_##test_name##_ForkTest()117118#define FORK_TEST(test_case_name, test_name) FORK_TEST_ON(test_case_name, test_name, NULL)119120// Run a test case fixture in a forked process, so that trapdoors don't121// affect other tests.122#define ICLASS_NAME(test_case_name, test_name) Forked##test_case_name##_##test_name123#define FORK_TEST_F(test_case_name, test_name) \124class ICLASS_NAME(test_case_name, test_name) : public test_case_name { \125public: \126ICLASS_NAME(test_case_name, test_name)() {} \127void InnerTestBody(); \128}; \129TEST_F(ICLASS_NAME(test_case_name, test_name), _) { \130_RUN_FORKED_MEM(this, \131ICLASS_NAME(test_case_name, test_name)::InnerTestBody, \132#test_case_name, #test_name); \133} \134void ICLASS_NAME(test_case_name, test_name)::InnerTestBody()135136// Emit errno information on failure137#define EXPECT_OK(v) EXPECT_LE(0, v) << " errno " << errno << " " << strerror(errno)138139// Expect a syscall to fail with the given error.140#define EXPECT_SYSCALL_FAIL(E, C) \141do { \142SCOPED_TRACE(#C); \143EXPECT_GT(0, C); \144EXPECT_EQ(E, errno) << "expected '" << strerror(E) \145<< "' but got '" << strerror(errno) << "'"; \146} while (0)147148// Expect a syscall to fail with anything other than the given error.149#define EXPECT_SYSCALL_FAIL_NOT(E, C) \150do { \151EXPECT_GT(0, C); \152EXPECT_NE(E, errno) << strerror(E); \153} while (0)154155// Expect a void syscall to fail with anything other than the given error.156#define EXPECT_VOID_SYSCALL_FAIL_NOT(E, C) \157do { \158errno = 0; \159C; \160EXPECT_NE(E, errno) << #C << " failed with ECAPMODE"; \161} while (0)162163// Expect a system call to fail due to path traversal; exact error164// code is OS-specific.165#ifdef O_BENEATH166#define EXPECT_OPENAT_FAIL_TRAVERSAL(fd, path, flags) \167do { \168SCOPED_TRACE(GTEST_STRINGIFY_(openat((fd), (path), (flags)))); \169const int result = openat((fd), (path), (flags)); \170if (((flags) & O_BENEATH) == O_BENEATH) { \171EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_O_BENEATH, result); \172} else { \173EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY, result); \174} \175if (result >= 0) { close(result); } \176} while (0)177#else178#define EXPECT_OPENAT_FAIL_TRAVERSAL(fd, path, flags) \179do { \180SCOPED_TRACE(GTEST_STRINGIFY_(openat((fd), (path), (flags)))); \181const int result = openat((fd), (path), (flags)); \182EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY, result); \183if (result >= 0) { close(result); } \184} while (0)185#endif186187// Expect a system call to fail with ECAPMODE.188#define EXPECT_CAPMODE(C) EXPECT_SYSCALL_FAIL(ECAPMODE, C)189190// Expect a system call to fail, but not with ECAPMODE.191#define EXPECT_FAIL_NOT_CAPMODE(C) EXPECT_SYSCALL_FAIL_NOT(ECAPMODE, C)192#define EXPECT_FAIL_VOID_NOT_CAPMODE(C) EXPECT_VOID_SYSCALL_FAIL_NOT(ECAPMODE, C)193194// Expect a system call to fail with ENOTCAPABLE.195#define EXPECT_NOTCAPABLE(C) EXPECT_SYSCALL_FAIL(ENOTCAPABLE, C)196197// Expect a system call to fail, but not with ENOTCAPABLE.198#define EXPECT_FAIL_NOT_NOTCAPABLE(C) EXPECT_SYSCALL_FAIL_NOT(ENOTCAPABLE, C)199200// Expect a system call to fail with either ENOTCAPABLE or ECAPMODE.201#define EXPECT_CAPFAIL(C) \202do { \203int rc = C; \204EXPECT_GT(0, rc); \205EXPECT_TRUE(errno == ECAPMODE || errno == ENOTCAPABLE) \206<< #C << " did not fail with ECAPMODE/ENOTCAPABLE but " << errno \207<< "(" << strerror(errno) << ")"; \208} while (0)209210// Ensure that 'rights' are a subset of 'max'.211#define EXPECT_RIGHTS_IN(rights, max) \212EXPECT_TRUE(cap_rights_contains((max), (rights))) \213<< "rights " << std::hex << *(rights) \214<< " not a subset of " << std::hex << *(max)215216// Ensure rights are identical217#define EXPECT_RIGHTS_EQ(a, b) \218do { \219EXPECT_RIGHTS_IN((a), (b)); \220EXPECT_RIGHTS_IN((b), (a)); \221} while (0)222223// Get the state of a process as a single character.224// - 'D': disk wait225// - 'R': runnable226// - 'S': sleeping/idle227// - 'T': stopped228// - 'Z': zombie229// On error, return either '?' or '\0'.230char ProcessState(int pid);231232// Check process state reaches a particular expected state (or two).233// Retries a few times to allow for timing issues.234#define EXPECT_PID_REACHES_STATES(pid, expected1, expected2) { \235int counter = 5; \236char state; \237do { \238state = ProcessState(pid); \239if (state == expected1 || state == expected2) break; \240usleep(100000); \241} while (--counter > 0); \242EXPECT_TRUE(state == expected1 || state == expected2) \243<< " pid " << pid << " in state " << state; \244}245246#define EXPECT_PID_ALIVE(pid) EXPECT_PID_REACHES_STATES(pid, 'R', 'S')247#define EXPECT_PID_DEAD(pid) EXPECT_PID_REACHES_STATES(pid, 'Z', '\0')248#define EXPECT_PID_ZOMBIE(pid) EXPECT_PID_REACHES_STATES(pid, 'Z', 'Z');249#define EXPECT_PID_GONE(pid) EXPECT_PID_REACHES_STATES(pid, '\0', '\0');250251enum {252// Magic numbers for messages sent by child processes.253MSG_CHILD_STARTED = 1234,254MSG_CHILD_FD_RECEIVED = 4321,255// Magic numbers for messages sent by parent processes.256MSG_PARENT_REQUEST_CHILD_EXIT = 9999,257MSG_PARENT_CLOSED_FD = 10000,258MSG_PARENT_CHILD_SHOULD_RUN = 10001,259};260261#define SEND_INT_MESSAGE(fd, message) \262do { \263int _msg = message; \264EXPECT_EQ(sizeof(_msg), (size_t)write(fd, &_msg, sizeof(_msg))); \265} while (0)266267#define AWAIT_INT_MESSAGE(fd, expected) \268do { \269int _msg = 0; \270EXPECT_EQ(sizeof(_msg), (size_t)read(fd, &_msg, sizeof(_msg))); \271EXPECT_EQ(expected, _msg); \272} while (0)273274// Mark a test that can only be run as root.275#define GTEST_SKIP_IF_NOT_ROOT() \276if (getuid() != 0) { GTEST_SKIP() << "requires root"; }277278extern std::string capsicum_test_bindir;279280#endif // CAPSICUM_TEST_H281282283