WIP/cpp -> next #31

Closed
crtxcr wants to merge 16 commits from WIP/cpp into next
7 changed files with 2277 additions and 1719 deletions

View File

@ -1,17 +1,27 @@
prefix = /usr/local prefix = /usr/local
bindir = $(prefix)/bin bindir = $(prefix)/bin
CFLAGS = -std=c99 -Wall -Wextra -pedantic CFLAGS = -std=c99 -Wall -Wextra -pedantic
CXXFLAGS = -std=c++20 -Wall -Wextra -pedantic
.DEFAULT_GOAL := test .DEFAULT_GOAL := tests
clean: clean:
rm -f test rm -f test exile.o testcpp
test: test.c
$(CC) test.c -g $(CFLAGS) -o test
check: test exile.o: exile.c exile.h
$(CC) -c exile.c -g $(CFLAGS) -o exile.o
test: test.c exile.h exile.o
$(CC) test.c exile.o -g $(CFLAGS) -o test
testcpp: test.cpp exile.h exile.hpp exile.o
$(CXX) test.cpp exile.o -g $(CXXFLAGS) -o testcpp
tests: test testcpp
check: tests
./test.sh ./test.sh
.PHONY: check .PHONY: check

1854
exile.c Normal file

File diff suppressed because it is too large Load Diff

1708
exile.h

File diff suppressed because it is too large Load Diff

199
exile.hpp Normal file
View File

@ -0,0 +1,199 @@
#include "exile.h"
#include <functional>
#include <iostream>
#include <string>
#include <tuple>
#include <memory>
#include <sys/wait.h>
#ifndef EXILE_MMAP_SIZE
#define EXILE_MMAP_SIZE 128 * 1024 * 1024 //128MB
#endif
template<typename T, typename U, typename ... Args>
class launch_arg
{
static_assert(std::is_trivially_copyable_v<T>);
static_assert(!std::is_pointer_v<T>);
public:
struct exile_policy *policy;
T *result_shm;
U fn;
std::tuple<Args...> args;
launch_arg(struct exile_policy *policy, T *result_shm, U fn, Args && ... args) : policy(policy),
result_shm(result_shm), fn(fn), args(std::forward<Args>(args)...) {}
};
template<typename T, typename U, typename ... Args>
class launch_arg_serializer
{
static_assert(std::is_copy_constructible_v<T>);
public:
struct exile_policy *policy;
char *serialize_buffer;
size_t n;
U fn;
std::tuple<Args...> args;
const std::function<size_t (const T &, char *, size_t n)> &serializer;
const std::function<T(const char * buf, size_t n)> &deserializer;
launch_arg_serializer(struct exile_policy *policy, char *serialize_buffer, size_t n, const std::function<size_t (const T &, char *, size_t)> &serializer, const std::function<T(const char *, size_t)> &deserializer, U fn, Args && ... args) : policy(policy), serialize_buffer(serialize_buffer), n(n), fn(fn), args(std::forward<Args>(args)...), serializer(serializer), deserializer(deserializer) {}
};
template<typename T, typename U, typename ... Args>
int exile_clone_handle_trivial(void * arg)
{
static_assert(std::is_trivially_copyable_v<T>);
static_assert(!std::is_pointer_v<T>);
launch_arg<T, U, Args...> *launchargs = (launch_arg<T, U, Args...> *) arg;
int ret = exile_enable_policy(launchargs->policy);
if(ret != 0)
{
EXILE_LOG_ERROR("exile_enable_policy() failed: %s\n", strerror(errno));
return 1;
}
T result = std::apply(launchargs->fn, launchargs->args);
std::cout << result;
memcpy(launchargs->result_shm, &result, sizeof(T));
return 0;
}
template<typename T, typename U, typename ... Args>
int exile_clone_handle_serializer(void * arg)
{
static_assert(std::is_copy_constructible_v<T>);
launch_arg_serializer<T, U, Args...> *launchargs = (launch_arg_serializer<T, U, Args...> *) arg;
int ret = exile_enable_policy(launchargs->policy);
if(ret != 0)
{
EXILE_LOG_ERROR("exile_enable_policy() failed: %s\n", strerror(errno));
return 1;
}
T result = std::apply(launchargs->fn, launchargs->args);
/* TODO: exception handling */
/* TODO: ugly :S */
char *target = launchargs->serialize_buffer + sizeof(size_t);
size_t n = launchargs->n - sizeof(size_t);
size_t size = launchargs->serializer(result, target, n);
memcpy(launchargs->serialize_buffer, &size, sizeof(size_t));
return 0;
}
inline int do_clone(int (*clonefn)(void *), void *launcharg)
{
struct rlimit rlimit;
int ret = getrlimit(RLIMIT_STACK, &rlimit);
if(ret != 0)
{
EXILE_LOG_ERROR("Failed to get stack size: %s\n", strerror(errno));
return ret;
}
size_t size = rlimit.rlim_cur;
char *stack = (char *) calloc(1, size);
if(stack == NULL)
{
EXILE_LOG_ERROR("Failed to allocate stack memory for child\n");
return 1;
}
stack += size;
ret = clone(clonefn, stack, 17 /* SIGCHLD */, launcharg);
int status = 0;
waitpid(ret, &status, __WALL);
if(WIFEXITED(status))
{
return WEXITSTATUS(status);
}
/* TODO: exception or what? */
return 23;
}
template<typename T, typename U, typename ... Args>
typename std::enable_if_t<std::is_trivially_copyable_v<T>, T> exile_launch(struct exile_policy *policy, U fn, Args && ... args)
{
size_t mapsize = sizeof(T);
T * sharedbuf = (T *) mmap(NULL, mapsize , PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
if(sharedbuf == NULL)
{
throw std::runtime_error(std::string("mmap failed: ") + strerror(errno));
}
std::shared_ptr<void> deleter(nullptr, [sharedbuf, mapsize](...){ munmap(sharedbuf, mapsize); });
launch_arg<T, U, Args...> launcharg(policy, sharedbuf, fn, std::forward<Args>(args)...);
int (*clonefn)(void *) = &exile_clone_handle_trivial<T, U, Args...>;
/* TODO: exception or what? */
int ret = do_clone(clonefn, &launcharg);
if(ret == 0)
{
return *sharedbuf;
}
throw std::runtime_error(std::string("clone() failed: " + std::to_string(ret)));
return T();
}
template<typename T, typename U, typename ... Args>
typename std::enable_if_t<!std::is_trivially_copyable_v<T> && std::is_copy_constructible_v<T>, T>
exile_launch(struct exile_policy *policy, const std::function<size_t (const T &, char *, size_t)> &serializer, const std::function<T(const char *, size_t)> &deserializer, U fn, Args && ... args)
{
size_t mapsize = EXILE_MMAP_SIZE;
char *sharedbuf = (char *) mmap(NULL, mapsize , PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
if(sharedbuf == NULL)
{
throw std::runtime_error(std::string("mmap failed: ") + strerror(errno));
}
std::shared_ptr<void> deleter(nullptr, [sharedbuf, mapsize](...){ munmap(sharedbuf, mapsize); });
launch_arg_serializer<T, U, Args...> launcharg(policy, sharedbuf, mapsize, serializer, deserializer, fn, std::forward<Args>(args)...);
int (*clonefn)(void *) = &exile_clone_handle_serializer<T, U, Args...>;
/* TODO: exception or what? */
int ret = do_clone(clonefn, &launcharg);
if(ret == 0)
{
size_t size = 0;
memcpy(&size, sharedbuf, sizeof(size));
return deserializer(sharedbuf + sizeof(size_t), size);
}
throw std::runtime_error(std::string("clone() failed: " + std::to_string(ret)));
return T();
}
template<class T>
std::basic_string<typename T::value_type> deserialize_stdstring(const char *buf, size_t n)
{
return std::basic_string<typename T::value_type> { buf, n };
}
template<class T>
size_t serialize_stdstring(const std::basic_string<typename T::value_type> &t, char *buf, size_t n)
{
if(n < t.size())
{
return 0;
}
memcpy(buf, t.data(), t.size());
return t.size();
}
template<typename T, typename U, typename ... Args>
std::basic_string<typename T::value_type> exile_launch(struct exile_policy *policy, U fn, Args && ... args)
{
return exile_launch<T, U, Args...>(policy, &serialize_stdstring<T>, &deserialize_stdstring<T>, fn, std::forward<Args>(args) ...);
}

105
test.c
View File

@ -6,12 +6,14 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/wait.h> #include <sys/wait.h>
#define LOG(...) do { fprintf(stdout, "%s(): ", __func__); fprintf(stdout, __VA_ARGS__); } while(0)
int xexile_enable_policy(struct exile_policy *policy) int xexile_enable_policy(struct exile_policy *policy)
{ {
int ret = exile_enable_policy(policy); int ret = exile_enable_policy(policy);
if(ret != 0) if(ret != 0)
{ {
fprintf(stderr, "exile_enable_policy() failed: %i\n", ret); LOG("failed: %i\n", ret);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
return 0; return 0;
@ -38,16 +40,16 @@ static int test_expected_kill(int (*f)())
int c = WTERMSIG(status); int c = WTERMSIG(status);
if(c == SIGSYS) if(c == SIGSYS)
{ {
printf("Got expected signal\n"); LOG("Got expected signal\n");
return 0; return 0;
} }
printf("Unexpected status code: %i\n", c); LOG("Unexpected status code: %i\n", c);
return 1; return 1;
} }
else else
{ {
int c = WEXITSTATUS(status); int c = WEXITSTATUS(status);
printf("Process was not killed, test fails. Status code of exit: %i\n", c); LOG("Process was not killed, test fails. Status code of exit: %i\n", c);
return 1; return 1;
} }
return 0; return 0;
@ -67,7 +69,7 @@ static int test_successful_exit(int (*f)())
if(WIFSIGNALED(status)) if(WIFSIGNALED(status))
{ {
int c = WTERMSIG(status); int c = WTERMSIG(status);
printf("Received signal, which was not expected. Signal was: %i\n", c); LOG("Received signal, which was not expected. Signal was: %i\n", c);
return 1; return 1;
} }
else else
@ -75,11 +77,11 @@ static int test_successful_exit(int (*f)())
int c = WEXITSTATUS(status); int c = WEXITSTATUS(status);
if(c != 0) if(c != 0)
{ {
printf("Process failed to exit properly. Status code is: %i\n", c); LOG("Process failed to exit properly. Status code is: %i\n", c);
} }
return c; return c;
} }
printf("Process exited sucessfully as expected"); LOG("Process exited sucessfully as expected");
return 0; return 0;
} }
@ -153,7 +155,7 @@ int test_seccomp_require_last_matchall()
int status = exile_enable_policy(policy); int status = exile_enable_policy(policy);
if(status == 0) if(status == 0)
{ {
printf("Failed. Should not have been enabled!"); LOG("Failed. Should not have been enabled!");
return 1; return 1;
} }
return 0; return 0;
@ -170,7 +172,7 @@ static int do_test_seccomp_errno()
uid_t id = syscall(EXILE_SYS(getuid)); uid_t id = syscall(EXILE_SYS(getuid));
int fd = syscall(EXILE_SYS(close), 0); int fd = syscall(EXILE_SYS(close), 0);
printf("close() return code: %i, errno: %s\n", fd, strerror(errno)); LOG("close() return code: %i, errno: %s\n", fd, strerror(errno));
return fd == -1 ? 0 : 1; return fd == -1 ? 0 : 1;
} }
@ -254,14 +256,14 @@ int test_seccomp_argfilter_mixed()
int s = (int) syscall(EXILE_SYS(stat), "/dev/urandom", &statbuf); int s = (int) syscall(EXILE_SYS(stat), "/dev/urandom", &statbuf);
if(s != -1) if(s != -1)
{ {
printf("Failed: stat was expected to fail, but returned %i\n", s); LOG("Failed: stat was expected to fail, but returned %i\n", s);
return 1; return 1;
} }
pid_t p = (pid_t) syscall(EXILE_SYS(getpid)); pid_t p = (pid_t) syscall(EXILE_SYS(getpid));
if(p != -1) if(p != -1)
{ {
printf("Failed: getpid was expected to fail, but returned %i\n", p); LOG("Failed: getpid was expected to fail, but returned %i\n", p);
return 1; return 1;
} }
@ -269,13 +271,13 @@ int test_seccomp_argfilter_mixed()
int ret = (int) syscall(EXILE_SYS(open),t, O_WRONLY); int ret = (int) syscall(EXILE_SYS(open),t, O_WRONLY);
if(ret != -1) if(ret != -1)
{ {
printf("Failed: open was expected to fail, but returned %i\n", ret); LOG("Failed: open was expected to fail, but returned %i\n", ret);
return 1; return 1;
} }
ret = (int) syscall(EXILE_SYS(open), t, O_RDONLY); ret = (int) syscall(EXILE_SYS(open), t, O_RDONLY);
if(ret == -1) if(ret == -1)
{ {
printf("Failed: open with O_RDONLY was expected to succeed, but returned %i\n", ret); LOG("Failed: open with O_RDONLY was expected to succeed, but returned %i\n", ret);
return 1; return 1;
} }
return 0; return 0;
@ -291,13 +293,13 @@ int do_test_seccomp_vow_socket()
int s = socket(AF_INET, SOCK_STREAM, 0); int s = socket(AF_INET, SOCK_STREAM, 0);
if(s == -1) if(s == -1)
{ {
printf("Failed: socket was expected to succeed, but returned %i\n", s); LOG("Failed: socket was expected to succeed, but returned %i\n", s);
return 1; return 1;
} }
s = socket(AF_UNIX, SOCK_DGRAM, 0); s = socket(AF_UNIX, SOCK_DGRAM, 0);
if(s != -1) if(s != -1)
{ {
printf("Failed: socket was expected to fail, but returned %i\n", s); LOG("Failed: socket was expected to fail, but returned %i\n", s);
return 1; return 1;
} }
return 0; return 0;
@ -312,19 +314,19 @@ int do_test_seccomp_vow_open()
int ret = open("/dev/urandom", O_WRONLY | O_APPEND); int ret = open("/dev/urandom", O_WRONLY | O_APPEND);
if(ret != -1) if(ret != -1)
{ {
printf("Failed: open was expected to fail, but returned %i\n", ret); LOG("Failed: open was expected to fail, but returned %i\n", ret);
return 1; return 1;
} }
ret = open("/dev/urandom", O_RDWR); ret = open("/dev/urandom", O_RDWR);
if(ret != -1) if(ret != -1)
{ {
printf("Failed: open O_RDWR was expected to fail, but returned %i\n", ret); LOG("Failed: open O_RDWR was expected to fail, but returned %i\n", ret);
return 1; return 1;
} }
ret = open("/dev/urandom", O_RDONLY); ret = open("/dev/urandom", O_RDONLY);
if(ret == -1) if(ret == -1)
{ {
printf("Failed: open was expected to succceed, but returned %i\n", ret); LOG("Failed: open was expected to succceed, but returned %i\n", ret);
return 1; return 1;
} }
return 0; return 0;
@ -335,13 +337,13 @@ int test_seccomp_vow()
int ret = test_successful_exit(&do_test_seccomp_vow_open); int ret = test_successful_exit(&do_test_seccomp_vow_open);
if(ret != 0) if(ret != 0)
{ {
printf("Failed: do_test_seccomp_vow_open()\n"); LOG("Failed: do_test_seccomp_vow_open()\n");
return 1; return 1;
} }
ret = test_successful_exit(&do_test_seccomp_vow_socket); ret = test_successful_exit(&do_test_seccomp_vow_socket);
if(ret != 0) if(ret != 0)
{ {
printf("Failed: do_test_seccomp_vow_socket()\n"); LOG("Failed: do_test_seccomp_vow_socket()\n");
return 1; return 1;
} }
return 0; return 0;
@ -353,13 +355,13 @@ int test_seccomp_exile_vow_multiple()
int ret = exile_vow(EXILE_SYSCALL_VOW_STDIO | EXILE_SYSCALL_VOW_UNIX | EXILE_SYSCALL_VOW_SECCOMP_INSTALL | EXILE_SYSCALL_VOW_DENY_ERROR); int ret = exile_vow(EXILE_SYSCALL_VOW_STDIO | EXILE_SYSCALL_VOW_UNIX | EXILE_SYSCALL_VOW_SECCOMP_INSTALL | EXILE_SYSCALL_VOW_DENY_ERROR);
if(ret != 0) if(ret != 0)
{ {
printf("Failed: exile_vow() call 1 failed\n"); LOG("Failed: exile_vow() call 1 failed\n");
return 1; return 1;
} }
int s = socket(AF_UNIX, SOCK_STREAM, 0); int s = socket(AF_UNIX, SOCK_STREAM, 0);
if(s == -1) if(s == -1)
{ {
printf("Failed: socket was expected to succeed, but returned %i\n", s); LOG("Failed: socket was expected to succeed, but returned %i\n", s);
return 1; return 1;
} }
@ -367,13 +369,13 @@ int test_seccomp_exile_vow_multiple()
ret = exile_vow(EXILE_SYSCALL_VOW_STDIO | EXILE_SYSCALL_VOW_SECCOMP_INSTALL | EXILE_SYSCALL_VOW_DENY_ERROR); ret = exile_vow(EXILE_SYSCALL_VOW_STDIO | EXILE_SYSCALL_VOW_SECCOMP_INSTALL | EXILE_SYSCALL_VOW_DENY_ERROR);
if(ret != 0) if(ret != 0)
{ {
printf("Failed: exile_vow() call 2 failed\n"); LOG("Failed: exile_vow() call 2 failed\n");
return 1; return 1;
} }
s = socket(AF_UNIX, SOCK_STREAM, 0); s = socket(AF_UNIX, SOCK_STREAM, 0);
if(s != -1) if(s != -1)
{ {
printf("Failed: socket was expected to fail, but returned %i\n", s); LOG("Failed: socket was expected to fail, but returned %i\n", s);
return 1; return 1;
} }
@ -381,13 +383,13 @@ int test_seccomp_exile_vow_multiple()
ret = exile_vow(EXILE_SYSCALL_VOW_STDIO | EXILE_SYSCALL_VOW_UNIX | EXILE_SYSCALL_VOW_SECCOMP_INSTALL | EXILE_SYSCALL_VOW_DENY_ERROR); ret = exile_vow(EXILE_SYSCALL_VOW_STDIO | EXILE_SYSCALL_VOW_UNIX | EXILE_SYSCALL_VOW_SECCOMP_INSTALL | EXILE_SYSCALL_VOW_DENY_ERROR);
if(ret != 0) if(ret != 0)
{ {
printf("Failed: exile_vow() call 3 failed\n"); LOG("Failed: exile_vow() call 3 failed\n");
return 1; return 1;
} }
s = socket(AF_UNIX, SOCK_STREAM, 0); s = socket(AF_UNIX, SOCK_STREAM, 0);
if(s != -1) if(s != -1)
{ {
printf("Failed: socket was still expected to fail, but returned %i\n", s); LOG("Failed: socket was still expected to fail, but returned %i\n", s);
return 1; return 1;
} }
@ -400,7 +402,7 @@ int test_landlock()
{ {
if(!exile_landlock_is_available()) if(!exile_landlock_is_available())
{ {
printf("landlock not available, so cannot test\n"); LOG("landlock not available, so cannot test\n");
return 1; return 1;
} }
struct exile_policy *policy = exile_init_policy(); struct exile_policy *policy = exile_init_policy();
@ -449,14 +451,14 @@ int test_nofs()
int s = socket(AF_INET,SOCK_STREAM,0); int s = socket(AF_INET,SOCK_STREAM,0);
if(s == -1) if(s == -1)
{ {
fprintf(stderr, "Failed to open socket but this was not requested by policy\n"); LOG("Failed to open socket but this was not requested by policy\n");
return 1; return 1;
} }
/* Expect seccomp to take care of this */ /* Expect seccomp to take care of this */
if(open("/test", O_CREAT | O_WRONLY) >= 0) if(open("/test", O_CREAT | O_WRONLY) >= 0)
{ {
fprintf(stderr, "Failed: We do not expect write access\n"); LOG("Failed: We do not expect write access\n");
return 1; return 1;
} }
@ -472,14 +474,14 @@ int test_no_new_fds()
if(open("/tmp/test", O_CREAT | O_WRONLY) >= 0) if(open("/tmp/test", O_CREAT | O_WRONLY) >= 0)
{ {
fprintf(stderr, "Failed: Could open new file descriptor\n"); LOG("Failed: Could open new file descriptor\n");
return -1; return -1;
} }
int s = socket(AF_INET,SOCK_STREAM,0); int s = socket(AF_INET,SOCK_STREAM,0);
if(s >= 0) if(s >= 0)
{ {
fprintf(stderr, "Failed: socket got opened but policy denied\n"); LOG("Failed: socket got opened but policy denied\n");
return -1; return -1;
} }
@ -487,6 +489,7 @@ int test_no_new_fds()
} }
extern int mkpath(const char *p, mode_t mode, int baseisfile);
int test_mkpath() int test_mkpath()
{ {
system("rm -rf /tmp/.exile.h/"); system("rm -rf /tmp/.exile.h/");
@ -495,13 +498,13 @@ int test_mkpath()
int ret = mkpath(filepath, 0700, 1); int ret = mkpath(filepath, 0700, 1);
if(ret != 0) if(ret != 0)
{ {
fprintf(stderr, "Failed: mkpath(file) returned: %i\n", ret); LOG("Failed: mkpath(file) returned: %i\n", ret);
return 1; return 1;
} }
ret = mkpath(dirpath, 0700, 0); ret = mkpath(dirpath, 0700, 0);
if(ret != 0) if(ret != 0)
{ {
fprintf(stderr, "Failed: mkpath(dirpath) returned: %i\n", ret); LOG("Failed: mkpath(dirpath) returned: %i\n", ret);
return 1; return 1;
} }
@ -509,23 +512,23 @@ int test_mkpath()
ret = stat(filepath, &statbuf); ret = stat(filepath, &statbuf);
if(ret != 0) if(ret != 0)
{ {
fprintf(stderr, "Failed: stat on filepath returned: %i\n", ret); LOG("Failed: stat on filepath returned: %i\n", ret);
return 1; return 1;
} }
if(!S_ISREG(statbuf.st_mode)) if(!S_ISREG(statbuf.st_mode))
{ {
fprintf(stderr, "Failed: mkpath did not create a file: %i\n", ret); LOG("Failed: mkpath did not create a file: %i\n", ret);
return 1; return 1;
} }
ret = stat(dirpath, &statbuf); ret = stat(dirpath, &statbuf);
if(ret != 0) if(ret != 0)
{ {
fprintf(stderr, "Failed: stat on dirpath returned: %i\n", ret); LOG("Failed: stat on dirpath returned: %i\n", ret);
return 1; return 1;
} }
if(!S_ISDIR(statbuf.st_mode)) if(!S_ISDIR(statbuf.st_mode))
{ {
fprintf(stderr, "Failed: mkpath did not create a directory: %i\n", ret); LOG("Failed: mkpath did not create a directory: %i\n", ret);
return 1; return 1;
} }
system("rm -rf /tmp/.exile.h/"); system("rm -rf /tmp/.exile.h/");
@ -545,12 +548,14 @@ int test_fail_flags()
return 0; return 0;
} }
static int *read_pipe = NULL;
int do_launch_test(void *arg) int do_launch_test(void *arg)
{ {
int num = *(int *)(arg); int num = *(int *)(arg);
num += 1; num += 1;
char buffer[512] = { 0 }; char buffer[512] = { 0 };
read(child_write_pipe[0], buffer, sizeof(buffer)-1); read(*read_pipe, buffer, sizeof(buffer)-1);
printf("Sandboxed +1: %i\n", num); printf("Sandboxed +1: %i\n", num);
printf("Echoing: %s\n", buffer); printf("Echoing: %s\n", buffer);
fflush(stdout); fflush(stdout);
@ -566,10 +571,11 @@ int test_launch()
params.func = &do_launch_test; params.func = &do_launch_test;
params.funcarg = &num; params.funcarg = &num;
params.policy = policy; params.policy = policy;
read_pipe = &params.child_write_pipe[0];
int launchfd = exile_launch(&params, &res); int launchfd = exile_launch(&params, &res);
if(launchfd < 0) if(launchfd < 0)
{ {
printf("Failed to launch\n"); LOG("Failed to launch\n");
return 1; return 1;
} }
@ -577,11 +583,11 @@ int test_launch()
write(res.write_fd, "1234", 4); write(res.write_fd, "1234", 4);
int s = read(res.read_fd, buffer, sizeof(buffer)-1); int s = read(res.read_fd, buffer, sizeof(buffer)-1);
write(1, buffer, s); write(1, buffer, s);
printf("Before wait, got: %i\n", s); LOG("Before wait, got: %i\n", s);
fflush(stdout); fflush(stdout);
if(strstr(buffer, "Echoing: 1234") == NULL) if(strstr(buffer, "Echoing: 1234") == NULL)
{ {
printf("Failed: Did not get back what we wrote\n"); LOG("Failed: Did not get back what we wrote\n");
} }
int status = 0; int status = 0;
waitpid(res.tid, &status, __WALL); waitpid(res.tid, &status, __WALL);
@ -614,12 +620,24 @@ int test_launch_get()
unsigned int len = strlen(LAUNCH_GET_TEST_STR); unsigned int len = strlen(LAUNCH_GET_TEST_STR);
if(n != strlen(LAUNCH_GET_TEST_STR)) if(n != strlen(LAUNCH_GET_TEST_STR))
{ {
printf("Lenght does does not match: %lu vs %u\n", n, len); LOG("Lenght does does not match: %lu vs %u\n", n, len);
return 1; return 1;
} }
if(strcmp(content, LAUNCH_GET_TEST_STR) != 0) if(strcmp(content, LAUNCH_GET_TEST_STR) != 0)
{ {
printf("Received content differs\n"); LOG("Received content differs\n");
return 1;
}
return 0;
}
int test_vows_from_str()
{
uint64_t expected = EXILE_SYSCALL_VOW_CHOWN | EXILE_SYSCALL_VOW_WPATH | EXILE_SYSCALL_VOW_INET | EXILE_SYSCALL_VOW_DENY_ERROR;
uint64_t actual = exile_vows_from_str("chown wpath inet error");
if(expected != actual)
{
LOG("Masks don't match: %lu vs %lu\n", expected, actual);
return 1; return 1;
} }
return 0; return 0;
@ -651,6 +669,7 @@ struct dispatcher dispatchers[] = {
{ "failflags", &test_fail_flags}, { "failflags", &test_fail_flags},
{ "launch", &test_launch}, { "launch", &test_launch},
{ "launch-get", &test_launch_get}, { "launch-get", &test_launch_get},
{ "vow_from_str", &test_vows_from_str},
}; };
int main(int argc, char *argv[]) int main(int argc, char *argv[])

92
test.cpp Normal file
View File

@ -0,0 +1,92 @@
#include "exile.hpp"
#include "assert.h"
#include <map>
std::string sandboxed_reverse(std::string str)
{
std::reverse(str.begin(), str.end());
return str;
}
size_t stdstrlen(const std::string &str)
{
return str.size();
}
int incrementer(int arg)
{
return ++arg;
}
int test_exile_launch_trivial()
{
int u = 22;
int result = exile_launch<int>(exile_init_policy(), &incrementer, u);
assert(result == 23);
return 0;
}
int test_exile_launch_stdstring()
{
std::string str = "abc123";
std::string reversed = exile_launch<std::string>(exile_init_policy(), &sandboxed_reverse, str);
assert(reversed == "321cba");
return 0;
}
struct not_trivially_copyable
{
public:
std::string somecontent;
};
int test_exile_launch_serializer()
{
static_assert(! std::is_trivially_copyable_v<not_trivially_copyable>);
auto serializer = [](const not_trivially_copyable &obj, char *buf, size_t n){
serialize_stdstring<std::string>(obj.somecontent, buf, n);
return obj.somecontent.size();
};
auto deserializer = [](const char *buffer, size_t n) {
not_trivially_copyable obj;
obj.somecontent = deserialize_stdstring<std::string>(buffer, n);
return obj;
};
not_trivially_copyable result = exile_launch<not_trivially_copyable>(exile_init_policy(), serializer, deserializer, []() {not_trivially_copyable obj; obj.somecontent = "Just something"; return obj;});
assert(result.somecontent == "Just something");
return 0;
}
int main(int argc, char *argv[])
{
if(argc < 2)
{
std::cerr << "Missing test" << std::endl;
return 1;
}
std::map<std::string, int (*)()> map = {
{ "launch-trivial-cpp", &test_exile_launch_trivial} ,
{ "launch-stdstring-cpp", &test_exile_launch_stdstring },
{ "launch-serializer-cpp", &test_exile_launch_serializer },
};
std::string test = argv[1];
if(test == "--dumptests")
{
for(auto &entry : map)
{
std::cout << entry.first << std::endl;
}
return 0;
}
int (*fn)() = map[test];
if(fn != nullptr)
{
return fn();
}
std::cerr << "Unknown test" << std::endl;
return 1;
}

18
test.sh
View File

@ -44,14 +44,15 @@ function runtest_skipped()
function runtest() function runtest()
{ {
testname="$1" testbin="$1"
test_log_file="$2" testname="$2"
test_log_file="$3"
echo "Running: $testname. Date: $(date)" > "${test_log_file}" echo "Running: $testname. Date: $(date)" > "${test_log_file}"
echo -n "Running $1... " echo -n "Running $testname... "
#exit $? to suppress shell message like "./test.sh: line 18: pid Bad system call" #exit $? to suppress shell message like "./test.sh: line 18: pid Bad system call"
(./test $1 || exit $?) &>> "${test_log_file}" (./$testbin "$testname" || exit $?) &>> "${test_log_file}"
ret=$? ret=$?
SUCCESS="no" SUCCESS="no"
if [ $ret -eq 0 ] ; then if [ $ret -eq 0 ] ; then
@ -64,7 +65,7 @@ function runtest()
runtest_fail runtest_fail
fi fi
echo "Finished: ${testname}. Date: $(date). Success: $SUCCESS" >> "${test_log_file}" echo "Finished: ${testname} (${testbin}). Date: $(date). Success: $SUCCESS" >> "${test_log_file}"
} }
GIT_ID=$( git log --pretty="format:%h" -n1 ) GIT_ID=$( git log --pretty="format:%h" -n1 )
@ -79,7 +80,12 @@ LOG_OUTPUT_DIR_PATH="${LOG_OUTPUT_DIR}/exile_test_${GIT_ID}_${TIMESTAMP}"
for test in $( ./test --dumptests ) ; do for test in $( ./test --dumptests ) ; do
testname=$( echo $test ) testname=$( echo $test )
runtest "$testname" "${LOG_OUTPUT_DIR_PATH}/log.${testname}" runtest test "$testname" "${LOG_OUTPUT_DIR_PATH}/log.${testname}"
done
for test in $( ./testcpp --dumptests ) ; do
testname=$( echo $test )
runtest testcpp "$testname" "${LOG_OUTPUT_DIR_PATH}/log.${testname}"
done done
echo echo
echo "Tests finished. Logs in $(realpath ${LOG_OUTPUT_DIR_PATH})" echo "Tests finished. Logs in $(realpath ${LOG_OUTPUT_DIR_PATH})"