diff --git a/repos/base-linux/lib/mk/base-linux.mk b/repos/base-linux/lib/mk/base-linux.mk index e9529685b..abfa2aebd 100644 --- a/repos/base-linux/lib/mk/base-linux.mk +++ b/repos/base-linux/lib/mk/base-linux.mk @@ -6,7 +6,7 @@ include $(REP_DIR)/lib/mk/base-linux.inc -LIBS += startup-linux base-linux-common cxx +LIBS += startup-linux base-linux-common cxx seccomp SRC_CC += thread.cc thread_myself.cc thread_linux.cc SRC_CC += capability_space.cc capability_raw.cc SRC_CC += attach_stack_area.cc diff --git a/repos/base-linux/lib/mk/spec/arm/seccomp.mk b/repos/base-linux/lib/mk/spec/arm/seccomp.mk new file mode 100644 index 000000000..50889af7a --- /dev/null +++ b/repos/base-linux/lib/mk/spec/arm/seccomp.mk @@ -0,0 +1,3 @@ +SRC_BIN += seccomp_bpf_policy.bin + +vpath seccomp_bpf_policy.bin $(REP_DIR)/src/lib/seccomp/spec/arm diff --git a/repos/base-linux/lib/mk/spec/x86_32/seccomp.mk b/repos/base-linux/lib/mk/spec/x86_32/seccomp.mk new file mode 100644 index 000000000..0886d43ed --- /dev/null +++ b/repos/base-linux/lib/mk/spec/x86_32/seccomp.mk @@ -0,0 +1,3 @@ +SRC_BIN += seccomp_bpf_policy.bin + +vpath seccomp_bpf_policy.bin $(REP_DIR)/src/lib/seccomp/spec/x86_32 diff --git a/repos/base-linux/lib/mk/spec/x86_64/seccomp.mk b/repos/base-linux/lib/mk/spec/x86_64/seccomp.mk new file mode 100644 index 000000000..c0902c8bd --- /dev/null +++ b/repos/base-linux/lib/mk/spec/x86_64/seccomp.mk @@ -0,0 +1,3 @@ +SRC_BIN += seccomp_bpf_policy.bin + +vpath seccomp_bpf_policy.bin $(REP_DIR)/src/lib/seccomp/spec/x86_64 diff --git a/repos/base-linux/src/lib/base/platform.cc b/repos/base-linux/src/lib/base/platform.cc index 82e843931..66fedb793 100644 --- a/repos/base-linux/src/lib/base/platform.cc +++ b/repos/base-linux/src/lib/base/platform.cc @@ -12,9 +12,51 @@ * under the terms of the GNU Affero General Public License version 3. */ +/* Genode includes */ +#include /* base-internal includes */ #include +#include +#include +#include /* prctl */ +#include /* seccomp's constants */ -void Genode::binary_ready_hook_for_platform() { } +using namespace Genode; + +extern char _binary_seccomp_bpf_policy_bin_start[]; +extern char _binary_seccomp_bpf_policy_bin_end[]; + +struct bpf_program { + Genode::uint16_t blk_cnt; + Genode::uint64_t* blks; +}; + +void Genode::binary_ready_hook_for_platform() +{ + if (lx_prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) { + error("PR_SET_NO_NEW_PRIVS failed"); + throw Exception(); + } + + for (char* i = _binary_seccomp_bpf_policy_bin_start; + i < _binary_seccomp_bpf_policy_bin_end - sizeof(Genode::uint32_t); i++) { + Genode::uint32_t* v = reinterpret_cast(i); + if (*v == 0xCAFEAFFE) { + *v = lx_getpid(); + } + } + + bpf_program program; + program.blk_cnt = (_binary_seccomp_bpf_policy_bin_end - + _binary_seccomp_bpf_policy_bin_start) / + sizeof(Genode::uint64_t); + program.blks = (Genode::uint64_t*)_binary_seccomp_bpf_policy_bin_start; + Genode::uint64_t flags = SECCOMP_FILTER_FLAG_TSYNC; + auto ret = lx_seccomp(SECCOMP_SET_MODE_FILTER, flags, &program); + if (ret != 0) { + error("SECCOMP_SET_MODE_FILTER failed ", ret); + throw Exception(); + } +} diff --git a/repos/base-linux/src/lib/base/thread_linux.cc b/repos/base-linux/src/lib/base/thread_linux.cc index bd70ecb6e..e6f9c4939 100644 --- a/repos/base-linux/src/lib/base/thread_linux.cc +++ b/repos/base-linux/src/lib/base/thread_linux.cc @@ -123,7 +123,10 @@ void Thread::_deinit_platform_thread() for (;;) { /* destroy thread locally */ - int ret = lx_tgkill(native_thread().pid, native_thread().tid, LX_SIGCANCEL); + int pid = native_thread().pid; + if (pid == 0) break; + + int ret = lx_tgkill(pid, native_thread().tid, LX_SIGCANCEL); if (ret < 0) break; diff --git a/repos/base-linux/src/lib/seccomp/seccomp_bpf_policy.h b/repos/base-linux/src/lib/seccomp/seccomp_bpf_policy.h new file mode 100644 index 000000000..bdb002f13 --- /dev/null +++ b/repos/base-linux/src/lib/seccomp/seccomp_bpf_policy.h @@ -0,0 +1,40 @@ +/* + * \brief Including seccomp filter policy binary + * \author Stefan Thoeni + * \date 2019-12-13 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * Copyright (C) 2019 gapfruit AG + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _LIB__SECCOMP_BPF_POLICY_H_ +#define _LIB__SECCOMP_BPF_POLICY_H_ + +#define STR2(x) #x +#define STR(x) STR2(x) + +#define INCBIN(name, file) \ + __asm__(".section .rodata\n" \ + ".global incbin_" STR(name) "_start\n" \ + ".type incbin_" STR(name) "_start, @object\n" \ + ".balign 16\n" \ + "incbin_" STR(name) "_start:\n" \ + ".incbin \"" file "\"\n" \ + \ + ".global incbin_" STR(name) "_end\n" \ + ".type incbin_" STR(name) "_end, @object\n" \ + ".balign 1\n" \ + "incbin_" STR(name) "_end:\n" \ + ".byte 0\n" \ + ); \ + extern const __attribute__((aligned(16))) void* incbin_ ## name ## _start; \ + extern const void* incbin_ ## name ## _end; \ + +INCBIN(seccomp_bpf_policy, "seccomp_bpf_policy.bin"); + +#endif /* _LIB__SECCOMP_BPF_POLICY_H_ */ diff --git a/repos/base-linux/src/lib/seccomp/spec/arm/seccomp_bpf_policy.bin b/repos/base-linux/src/lib/seccomp/spec/arm/seccomp_bpf_policy.bin new file mode 100644 index 000000000..2303a9af3 Binary files /dev/null and b/repos/base-linux/src/lib/seccomp/spec/arm/seccomp_bpf_policy.bin differ diff --git a/repos/base-linux/src/lib/seccomp/spec/x86_32/seccomp_bpf_policy.bin b/repos/base-linux/src/lib/seccomp/spec/x86_32/seccomp_bpf_policy.bin new file mode 100644 index 000000000..38a12d4e0 Binary files /dev/null and b/repos/base-linux/src/lib/seccomp/spec/x86_32/seccomp_bpf_policy.bin differ diff --git a/repos/base-linux/src/lib/seccomp/spec/x86_64/seccomp_bpf_policy.bin b/repos/base-linux/src/lib/seccomp/spec/x86_64/seccomp_bpf_policy.bin new file mode 100644 index 000000000..0264978c1 Binary files /dev/null and b/repos/base-linux/src/lib/seccomp/spec/x86_64/seccomp_bpf_policy.bin differ diff --git a/repos/base-linux/src/lib/seccomp/update.sh b/repos/base-linux/src/lib/seccomp/update.sh new file mode 100755 index 000000000..b5ebe4e31 --- /dev/null +++ b/repos/base-linux/src/lib/seccomp/update.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# Build and update seccomp Berkeley Packet Filter binary +# for use in base-linux + +SCRIPT_FILE=$(realpath "$0") +SCRIPT_DIR=$(dirname $SCRIPT_FILE) + +make -C $SCRIPT_DIR/../../../../../tool/seccomp \ +&& cp $SCRIPT_DIR/../../../../../tool/seccomp/seccomp_bpf_policy_x86_32.bin $SCRIPT_DIR/spec/x86_32/seccomp_bpf_policy.bin\ +&& cp $SCRIPT_DIR/../../../../../tool/seccomp/seccomp_bpf_policy_x86_64.bin $SCRIPT_DIR/spec/x86_64/seccomp_bpf_policy.bin\ +&& cp $SCRIPT_DIR/../../../../../tool/seccomp/seccomp_bpf_policy_arm.bin $SCRIPT_DIR/spec/arm/seccomp_bpf_policy.bin \ +&& make -C $SCRIPT_DIR/../../../../../tool/seccomp clean \ +|| exit $? + diff --git a/repos/base-linux/src/lib/syscall/linux_syscalls.h b/repos/base-linux/src/lib/syscall/linux_syscalls.h index 6cac5d359..08f9eacfe 100644 --- a/repos/base-linux/src/lib/syscall/linux_syscalls.h +++ b/repos/base-linux/src/lib/syscall/linux_syscalls.h @@ -522,4 +522,16 @@ inline bool lx_sigsetmask(int signum, bool state) } +inline int lx_prctl(int option, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5) +{ + return lx_syscall(SYS_prctl, option, arg2, arg3, arg4, arg5); +} + + +inline int lx_seccomp(int option, int flag, void* program) +{ + return lx_syscall(SYS_seccomp, option, flag, program); +} + #endif /* _LIB__SYSCALL__LINUX_SYSCALLS_H_ */ diff --git a/tool/seccomp/Makefile b/tool/seccomp/Makefile new file mode 100644 index 000000000..f996bc236 --- /dev/null +++ b/tool/seccomp/Makefile @@ -0,0 +1,13 @@ +.DEFAULT_GOAL := seccomp_bpf_filters + +seccomp_bpf_filters: seccomp_bpf_policy_x86_32.bin seccomp_bpf_policy_x86_64.bin seccomp_bpf_policy_arm.bin + +seccomp_bpf_policy_%.bin: seccomp_bpf_compiler_%.prg + ./$< > $@ + +seccomp_bpf_compiler_%.prg: seccomp_bpf_compiler_%.cc + @g++ $< -o $@ -lseccomp + +clean: + @rm seccomp_bpf_policy_*.bin 2> /dev/null; true + @rm seccomp_bpf_compiler_*.prg 2> /dev/null; true diff --git a/tool/seccomp/seccomp_bpf_compiler.h b/tool/seccomp/seccomp_bpf_compiler.h new file mode 100644 index 000000000..c666547c1 --- /dev/null +++ b/tool/seccomp/seccomp_bpf_compiler.h @@ -0,0 +1,198 @@ +/* + * \brief Generate seccomp filter policy for base-linux + * \author Stefan Thoeni + * \date 2019-12-13 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * Copyright (C) 2019 gapfruit AG + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include /* printf */ +#include /* libseccomp */ +#include +#include + +class Filter +{ + private: + scmp_filter_ctx _ctx = seccomp_init(SCMP_ACT_KILL_PROCESS); + uint32_t _arch; + + + void _add_allow_rule(int syscall_number) + { + int result = seccomp_rule_add(_ctx, SCMP_ACT_ALLOW, syscall_number, 0); + if (result != 0) { + fprintf(stderr, "Add rule failed for number %i\n", syscall_number); + throw -102; + } + } + + + void _add_allow_rule(int syscall_number, scmp_arg_cmp c1) + { + int result = seccomp_rule_add(_ctx, SCMP_ACT_ALLOW, syscall_number, 1, c1); + if (result != 0) { + fprintf(stderr, "Add rule failed for number %i\n", syscall_number); + throw -102; + } + } + + + void _add_allow_rule(int syscall_number, scmp_arg_cmp c1, scmp_arg_cmp c2) + { + int result = seccomp_rule_add(_ctx, SCMP_ACT_ALLOW, syscall_number, 2, c1, c2); + if (result != 0) { + fprintf(stderr, "Add rule failed for number %i\n", syscall_number); + throw -102; + } + } + + public: + + Filter(uint32_t arch) + : _arch(arch) + { + } + + + int create() + { + /* Kill the process if the filter architecture does not fit. */ + if (seccomp_attr_set(_ctx, SCMP_FLTATR_ACT_BADARCH, SCMP_ACT_KILL_PROCESS) != 0) { + fprintf(stderr, "Failed to set bad architecture action\n"); + throw -103; + } + + /* Remove the default architecture (e.g. native architecture) from the filter.*/ + if (seccomp_arch_remove(_ctx, SCMP_ARCH_NATIVE) != 0) { + fprintf(stderr, "Failed to remove default architecture\n"); + throw -103; + } + + /* Add the desired architecture to the filter.*/ + if (seccomp_arch_add(_ctx, _arch) != 0) { + fprintf(stderr, "Failed to add architecture\n"); + throw -103; + } + + /* This syscall is safe as it create a socket pair in the + * process */ + _add_allow_rule(SCMP_SYS(socketpair)); + + /* These syscalls should be safe as they only access already open sockets. */ + _add_allow_rule(SCMP_SYS(sendmsg)); + _add_allow_rule(SCMP_SYS(recvmsg)); + _add_allow_rule(SCMP_SYS(write)); + _add_allow_rule(SCMP_SYS(poll)); + _add_allow_rule(SCMP_SYS(epoll_create)); + _add_allow_rule(SCMP_SYS(epoll_ctl)); + _add_allow_rule(SCMP_SYS(epoll_wait)); + _add_allow_rule(SCMP_SYS(close)); + _add_allow_rule(SCMP_SYS(munmap)); + _add_allow_rule(SCMP_SYS(dup)); + _add_allow_rule(SCMP_SYS(fstat)); + _add_allow_rule(SCMP_SYS(fstat64)); + + /* This syscall is used to wait for a condition. This should be safe. */ + _add_allow_rule(SCMP_SYS(futex)); + + /* This syscall ends the program. This should be safe */ + _add_allow_rule(SCMP_SYS(exit)); + + /* These syscalls are used to react to signals. They should be safe */ + _add_allow_rule(SCMP_SYS(sigaltstack)); + _add_allow_rule(SCMP_SYS(rt_sigaction)); + + /* This syscall is used to sleep. This should be safe */ + _add_allow_rule(SCMP_SYS(nanosleep)); + + /* These syscall allow access to global information. We would like + * to reduced this. */ + _add_allow_rule(SCMP_SYS(getpid)); + _add_allow_rule(SCMP_SYS(gettid)); + _add_allow_rule(SCMP_SYS(gettimeofday)); + _add_allow_rule(SCMP_SYS(getpeername)); + + int clone_flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND + | CLONE_THREAD | CLONE_SYSVSEM; + + switch (_arch) + { + case SCMP_ARCH_X86: + { + /* The tgkill syscall must be made safe by restricting parameters. + * Genode uses LX_SIGCANCEL alias SIGRTMIN to cancel threads + * the 0xCAFEAFFE will be replaced with the process ID to restrict + * tgkill to the process (= thread group). */ + _add_allow_rule(SCMP_SYS(tgkill), SCMP_CMP32(0, SCMP_CMP_EQ, 0xCAFEAFFE), + SCMP_CMP32(2, SCMP_CMP_EQ, SIGRTMIN)); + + /* The clone syscall must be made safe by restricting parameters + * The specified flags only allow creation of new threads. */ + _add_allow_rule(SCMP_SYS(clone), SCMP_CMP32(0, SCMP_CMP_EQ, clone_flags)); + + /* The nmap syscall has a different name on different architectures + * but it slould be save as it only uses an already open socket. */ + _add_allow_rule(SCMP_SYS(mmap2)); + } + break; + case SCMP_ARCH_X86_64: + { + /* The tgkill syscall must be made safe by restricting parameters. + * Genode uses LX_SIGCANCEL alias SIGRTMIN to cancel threads + * the 0xCAFEAFFE will be replaced with the process ID to restrict + * tgkill to the process (= thread group). */ + _add_allow_rule(SCMP_SYS(tgkill), SCMP_CMP64(0, SCMP_CMP_EQ, 0xCAFEAFFE), + SCMP_CMP64(2, SCMP_CMP_EQ, SIGRTMIN)); + + /* The clone syscall must be made safe by restricting parameters + * The specified flags only allow creation of new threads. */ + _add_allow_rule(SCMP_SYS(clone), SCMP_CMP64(0, SCMP_CMP_EQ, clone_flags)); + + /* The nmap syscall has a different name on different architectures + * but it slould be save as it only uses an already open socket. */ + _add_allow_rule(SCMP_SYS(mmap)); + } + break; + case SCMP_ARCH_ARM: + { + /* The tgkill syscall must be made safe by restricting parameters. + * Genode uses LX_SIGCANCEL alias SIGRTMIN to cancel threads + * the 0xCAFEAFFE will be replaced with the process ID to restrict + * tgkill to the process (= thread group). */ + _add_allow_rule(SCMP_SYS(tgkill), SCMP_CMP32(0, SCMP_CMP_EQ, 0xCAFEAFFE), + SCMP_CMP32(2, SCMP_CMP_EQ, SIGRTMIN)); + + /* The clone syscall must be made safe by restricting parameters + * The specified flags only allow creation of new threads. */ + _add_allow_rule(SCMP_SYS(clone), SCMP_CMP32(0, SCMP_CMP_EQ, clone_flags)); + + /* The nmap2 syscall has a different name on different architectures + * but it slould be save as it only uses an already open socket. */ + _add_allow_rule(SCMP_SYS(mmap2)); + + /* This syscall is only used on ARM. */ + _add_allow_rule(SCMP_SYS(cacheflush)); + + /* This syscall is only used on ARM. */ + _add_allow_rule(SCMP_SYS(sigreturn)); + } + break; + default: + fprintf(stderr, "Unsupported architecture\n"); + throw -104; + } + + // build and export + seccomp_export_bpf(_ctx, 1); + + return 0; + } +}; + diff --git a/tool/seccomp/seccomp_bpf_compiler_arm.cc b/tool/seccomp/seccomp_bpf_compiler_arm.cc new file mode 100644 index 000000000..d47f17ae1 --- /dev/null +++ b/tool/seccomp/seccomp_bpf_compiler_arm.cc @@ -0,0 +1,23 @@ +/* + * \brief Generate seccomp filter policy for base-linux on arm + * \author Stefan Thoeni + * \date 2019-12-13 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * Copyright (C) 2019 gapfruit AG + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include /* printf */ +#include /* libseccomp */ +#include "seccomp_bpf_compiler.h" + +int main() +{ + Filter filter(SCMP_ARCH_ARM); + return filter.create(); +} diff --git a/tool/seccomp/seccomp_bpf_compiler_x86_32.cc b/tool/seccomp/seccomp_bpf_compiler_x86_32.cc new file mode 100644 index 000000000..f3e4717f3 --- /dev/null +++ b/tool/seccomp/seccomp_bpf_compiler_x86_32.cc @@ -0,0 +1,23 @@ +/* + * \brief Generate seccomp filter policy for base-linux on x86_64 + * \author Stefan Thoeni + * \date 2019-12-13 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * Copyright (C) 2019 gapfruit AG + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include /* printf */ +#include /* libseccomp */ +#include "seccomp_bpf_compiler.h" + +int main() +{ + Filter filter(SCMP_ARCH_X86); + return filter.create(); +} diff --git a/tool/seccomp/seccomp_bpf_compiler_x86_64.cc b/tool/seccomp/seccomp_bpf_compiler_x86_64.cc new file mode 100644 index 000000000..120cb0d2a --- /dev/null +++ b/tool/seccomp/seccomp_bpf_compiler_x86_64.cc @@ -0,0 +1,23 @@ +/* + * \brief Generate seccomp filter policy for base-linux on x86_64 + * \author Stefan Thoeni + * \date 2019-12-13 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * Copyright (C) 2019 gapfruit AG + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include /* printf */ +#include /* libseccomp */ +#include "seccomp_bpf_compiler.h" + +int main() +{ + Filter filter(SCMP_ARCH_X86_64); + return filter.create(); +}