WIP/cpp -> next #31
20
Makefile
20
Makefile
@ -1,17 +1,27 @@
|
||||
prefix = /usr/local
|
||||
bindir = $(prefix)/bin
|
||||
CFLAGS = -std=c99 -Wall -Wextra -pedantic
|
||||
CXXFLAGS = -std=c++20 -Wall -Wextra -pedantic
|
||||
|
||||
.DEFAULT_GOAL := test
|
||||
.DEFAULT_GOAL := tests
|
||||
|
||||
|
||||
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
|
||||
|
||||
.PHONY: check
|
||||
|
1854
exile.c
Normal file
1854
exile.c
Normal file
Різницю між файлами не показано, бо вона завелика
Завантажити різницю
1708
exile.h
1708
exile.h
Різницю між файлами не показано, бо вона завелика
Завантажити різницю
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/wait.h>
|
||||
|
||||
#define LOG(...) do { fprintf(stdout, "%s(): ", __func__); fprintf(stdout, __VA_ARGS__); } while(0)
|
||||
|
||||
int xexile_enable_policy(struct exile_policy *policy)
|
||||
{
|
||||
int ret = exile_enable_policy(policy);
|
||||
if(ret != 0)
|
||||
{
|
||||
fprintf(stderr, "exile_enable_policy() failed: %i\n", ret);
|
||||
LOG("failed: %i\n", ret);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return 0;
|
||||
@ -38,16 +40,16 @@ static int test_expected_kill(int (*f)())
|
||||
int c = WTERMSIG(status);
|
||||
if(c == SIGSYS)
|
||||
{
|
||||
printf("Got expected signal\n");
|
||||
LOG("Got expected signal\n");
|
||||
return 0;
|
||||
}
|
||||
printf("Unexpected status code: %i\n", c);
|
||||
LOG("Unexpected status code: %i\n", c);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
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 0;
|
||||
@ -67,7 +69,7 @@ static int test_successful_exit(int (*f)())
|
||||
if(WIFSIGNALED(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;
|
||||
}
|
||||
else
|
||||
@ -75,11 +77,11 @@ static int test_successful_exit(int (*f)())
|
||||
int c = WEXITSTATUS(status);
|
||||
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;
|
||||
}
|
||||
printf("Process exited sucessfully as expected");
|
||||
LOG("Process exited sucessfully as expected");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -153,7 +155,7 @@ int test_seccomp_require_last_matchall()
|
||||
int status = exile_enable_policy(policy);
|
||||
if(status == 0)
|
||||
{
|
||||
printf("Failed. Should not have been enabled!");
|
||||
LOG("Failed. Should not have been enabled!");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@ -170,7 +172,7 @@ static int do_test_seccomp_errno()
|
||||
uid_t id = syscall(EXILE_SYS(getuid));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -254,14 +256,14 @@ int test_seccomp_argfilter_mixed()
|
||||
int s = (int) syscall(EXILE_SYS(stat), "/dev/urandom", &statbuf);
|
||||
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;
|
||||
}
|
||||
|
||||
pid_t p = (pid_t) syscall(EXILE_SYS(getpid));
|
||||
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;
|
||||
}
|
||||
|
||||
@ -269,13 +271,13 @@ int test_seccomp_argfilter_mixed()
|
||||
int ret = (int) syscall(EXILE_SYS(open),t, O_WRONLY);
|
||||
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;
|
||||
}
|
||||
ret = (int) syscall(EXILE_SYS(open), t, O_RDONLY);
|
||||
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 0;
|
||||
@ -291,13 +293,13 @@ int do_test_seccomp_vow_socket()
|
||||
int s = socket(AF_INET, SOCK_STREAM, 0);
|
||||
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;
|
||||
}
|
||||
s = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
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 0;
|
||||
@ -312,19 +314,19 @@ int do_test_seccomp_vow_open()
|
||||
int ret = open("/dev/urandom", O_WRONLY | O_APPEND);
|
||||
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;
|
||||
}
|
||||
ret = open("/dev/urandom", O_RDWR);
|
||||
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;
|
||||
}
|
||||
ret = open("/dev/urandom", O_RDONLY);
|
||||
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 0;
|
||||
@ -335,13 +337,13 @@ int test_seccomp_vow()
|
||||
int ret = test_successful_exit(&do_test_seccomp_vow_open);
|
||||
if(ret != 0)
|
||||
{
|
||||
printf("Failed: do_test_seccomp_vow_open()\n");
|
||||
LOG("Failed: do_test_seccomp_vow_open()\n");
|
||||
return 1;
|
||||
}
|
||||
ret = test_successful_exit(&do_test_seccomp_vow_socket);
|
||||
if(ret != 0)
|
||||
{
|
||||
printf("Failed: do_test_seccomp_vow_socket()\n");
|
||||
LOG("Failed: do_test_seccomp_vow_socket()\n");
|
||||
return 1;
|
||||
}
|
||||
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);
|
||||
if(ret != 0)
|
||||
{
|
||||
printf("Failed: exile_vow() call 1 failed\n");
|
||||
LOG("Failed: exile_vow() call 1 failed\n");
|
||||
return 1;
|
||||
}
|
||||
int s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
if(ret != 0)
|
||||
{
|
||||
printf("Failed: exile_vow() call 2 failed\n");
|
||||
LOG("Failed: exile_vow() call 2 failed\n");
|
||||
return 1;
|
||||
}
|
||||
s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
if(ret != 0)
|
||||
{
|
||||
printf("Failed: exile_vow() call 3 failed\n");
|
||||
LOG("Failed: exile_vow() call 3 failed\n");
|
||||
return 1;
|
||||
}
|
||||
s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -400,7 +402,7 @@ int test_landlock()
|
||||
{
|
||||
if(!exile_landlock_is_available())
|
||||
{
|
||||
printf("landlock not available, so cannot test\n");
|
||||
LOG("landlock not available, so cannot test\n");
|
||||
return 1;
|
||||
}
|
||||
struct exile_policy *policy = exile_init_policy();
|
||||
@ -449,14 +451,14 @@ int test_nofs()
|
||||
int s = socket(AF_INET,SOCK_STREAM,0);
|
||||
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;
|
||||
}
|
||||
|
||||
/* Expect seccomp to take care of this */
|
||||
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;
|
||||
}
|
||||
|
||||
@ -472,14 +474,14 @@ int test_no_new_fds()
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int s = socket(AF_INET,SOCK_STREAM,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;
|
||||
}
|
||||
|
||||
@ -487,6 +489,7 @@ int test_no_new_fds()
|
||||
|
||||
}
|
||||
|
||||
extern int mkpath(const char *p, mode_t mode, int baseisfile);
|
||||
int test_mkpath()
|
||||
{
|
||||
system("rm -rf /tmp/.exile.h/");
|
||||
@ -495,13 +498,13 @@ int test_mkpath()
|
||||
int ret = mkpath(filepath, 0700, 1);
|
||||
if(ret != 0)
|
||||
{
|
||||
fprintf(stderr, "Failed: mkpath(file) returned: %i\n", ret);
|
||||
LOG("Failed: mkpath(file) returned: %i\n", ret);
|
||||
return 1;
|
||||
}
|
||||
ret = mkpath(dirpath, 0700, 0);
|
||||
if(ret != 0)
|
||||
{
|
||||
fprintf(stderr, "Failed: mkpath(dirpath) returned: %i\n", ret);
|
||||
LOG("Failed: mkpath(dirpath) returned: %i\n", ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -509,23 +512,23 @@ int test_mkpath()
|
||||
ret = stat(filepath, &statbuf);
|
||||
if(ret != 0)
|
||||
{
|
||||
fprintf(stderr, "Failed: stat on filepath returned: %i\n", ret);
|
||||
LOG("Failed: stat on filepath returned: %i\n", ret);
|
||||
return 1;
|
||||
}
|
||||
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;
|
||||
}
|
||||
ret = stat(dirpath, &statbuf);
|
||||
if(ret != 0)
|
||||
{
|
||||
fprintf(stderr, "Failed: stat on dirpath returned: %i\n", ret);
|
||||
LOG("Failed: stat on dirpath returned: %i\n", ret);
|
||||
return 1;
|
||||
}
|
||||
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;
|
||||
}
|
||||
system("rm -rf /tmp/.exile.h/");
|
||||
@ -545,12 +548,14 @@ int test_fail_flags()
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int *read_pipe = NULL;
|
||||
int do_launch_test(void *arg)
|
||||
{
|
||||
int num = *(int *)(arg);
|
||||
num += 1;
|
||||
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("Echoing: %s\n", buffer);
|
||||
fflush(stdout);
|
||||
@ -566,10 +571,11 @@ int test_launch()
|
||||
params.func = &do_launch_test;
|
||||
params.funcarg = #
|
||||
params.policy = policy;
|
||||
read_pipe = ¶ms.child_write_pipe[0];
|
||||
int launchfd = exile_launch(¶ms, &res);
|
||||
if(launchfd < 0)
|
||||
{
|
||||
printf("Failed to launch\n");
|
||||
LOG("Failed to launch\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -577,11 +583,11 @@ int test_launch()
|
||||
write(res.write_fd, "1234", 4);
|
||||
int s = read(res.read_fd, buffer, sizeof(buffer)-1);
|
||||
write(1, buffer, s);
|
||||
printf("Before wait, got: %i\n", s);
|
||||
LOG("Before wait, got: %i\n", s);
|
||||
fflush(stdout);
|
||||
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;
|
||||
waitpid(res.tid, &status, __WALL);
|
||||
@ -614,12 +620,24 @@ int test_launch_get()
|
||||
unsigned int len = 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;
|
||||
}
|
||||
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 0;
|
||||
@ -651,6 +669,7 @@ struct dispatcher dispatchers[] = {
|
||||
{ "failflags", &test_fail_flags},
|
||||
{ "launch", &test_launch},
|
||||
{ "launch-get", &test_launch_get},
|
||||
{ "vow_from_str", &test_vows_from_str},
|
||||
};
|
||||
|
||||
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()
|
||||
{
|
||||
testname="$1"
|
||||
test_log_file="$2"
|
||||
testbin="$1"
|
||||
testname="$2"
|
||||
test_log_file="$3"
|
||||
|
||||
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"
|
||||
(./test $1 || exit $?) &>> "${test_log_file}"
|
||||
(./$testbin "$testname" || exit $?) &>> "${test_log_file}"
|
||||
ret=$?
|
||||
SUCCESS="no"
|
||||
if [ $ret -eq 0 ] ; then
|
||||
@ -64,7 +65,7 @@ function runtest()
|
||||
runtest_fail
|
||||
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 )
|
||||
@ -79,7 +80,12 @@ LOG_OUTPUT_DIR_PATH="${LOG_OUTPUT_DIR}/exile_test_${GIT_ID}_${TIMESTAMP}"
|
||||
|
||||
for test in $( ./test --dumptests ) ; do
|
||||
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
|
||||
echo
|
||||
echo "Tests finished. Logs in $(realpath ${LOG_OUTPUT_DIR_PATH})"
|
||||
|
Завантаження…
Посилання в новій задачі
Block a user