WIP/cpp -> next #31
20
Makefile
20
Makefile
@ -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
|
||||||
|
199
exile.hpp
Normal file
199
exile.hpp
Normal 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
105
test.c
@ -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 = #
|
params.funcarg = #
|
||||||
params.policy = policy;
|
params.policy = policy;
|
||||||
|
read_pipe = ¶ms.child_write_pipe[0];
|
||||||
int launchfd = exile_launch(¶ms, &res);
|
int launchfd = exile_launch(¶ms, &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
92
test.cpp
Normal 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
18
test.sh
@ -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})"
|
||||||
|
Loading…
Reference in New Issue
Block a user