比较提交

..

16 次代码提交

作者 SHA1 备注 提交日期
ea66ef76eb exile_flags_to_landlock(): Cover more with ALL_WRITE, except devices
More consistent with mount(), where MS_NODEV disallows those.

We may need to introduce a flag that simply allows everything
2022-03-17 15:47:22 +01:00
66def7a28f append_syscall_to_bpf(): Check for unlikely case of too many sock_filters 2022-03-17 15:47:22 +01:00
dbf8e87440 exile.hpp: Mark do_clone inline, not static 2022-03-17 15:47:22 +01:00
98421fab90 Makefile: Build exile.o separately, link it in all tests 2022-03-17 15:47:22 +01:00
70c3fef500 exile.h: Retire static child_read/write_pipe vars 2022-03-17 15:47:22 +01:00
69829374c7 exile.h: Move definitions to new file exile.c
Especially with exile_launch(), we will be included
from more than one translation unit. Thus, ODR becomes
a headache now.

So move definitions to exile.c.
2022-03-17 15:47:22 +01:00
005851c645 exile.h: Add extern "C" guards 2022-03-17 15:47:22 +01:00
95fa11e928 c++: Add explicit exile_launch() std::basic_string variant 2022-03-17 15:47:22 +01:00
97e2025758 c++: Retire exile_launch_trivial(), use std::enable_if 2022-03-17 15:47:22 +01:00
8cfb73568a Makefile: Add 'tests' target, depend on headers too to rebuild on changes of those 2022-03-17 15:47:22 +01:00
e7a5ba7f7f test.sh: Also run C++ tests 2022-03-17 15:47:22 +01:00
e52eda186b Add test.cpp to test C++ API 2022-03-17 15:47:22 +01:00
90ed5bbae9 Begin C++ API: Add exile.hpp with exile_launch() wrappers 2022-03-17 15:47:22 +01:00
48b6de9036 struct syscall_vow_map: change 'str' to const char* 2022-03-17 15:47:22 +01:00
93acb13929 test: Introduce LOG(), avoid inconsistent printf/fprintf 2022-03-17 15:47:22 +01:00
9247a6636b Introduce exile_vows_from_str() 2022-03-17 15:47:22 +01:00
共有 7 个文件被更改,包括 2210 次插入1739 次删除

查看文件

@ -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

1857
exile.c 普通文件

文件差异内容过多而无法显示 加载差异

1757
exile.h

文件差异内容过多而无法显示 加载差异

199
exile.hpp 普通文件
查看文件

@ -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) ...);
}

6
test.c
查看文件

@ -489,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/");
@ -547,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);
@ -568,6 +571,7 @@ int test_launch()
params.func = &do_launch_test;
params.funcarg = &num;
params.policy = policy;
read_pipe = &params.child_write_pipe[0];
int launchfd = exile_launch(&params, &res);
if(launchfd < 0)
{

92
test.cpp 普通文件
查看文件

@ -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
查看文件

@ -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})"