From 0678cd78c91bbba318e9b32d53388be3d6fcf546 Mon Sep 17 00:00:00 2001 From: Tom Hromatka Date: Tue, 20 Feb 2018 09:59:41 -0700 Subject: [PATCH 1/7] api: Add support for SCMP_ACT_KILL_PROCESS This patch adds support for killing the entire process via the SCMP_ACT_KILL_PROCESS action. To maintain backward compatibility, SCMP_ACT_KILL defaults to SCMP_ACT_KILL_THREAD. Signed-off-by: Tom Hromatka --- doc/man/man3/seccomp_init.3 | 4 ++ doc/man/man3/seccomp_rule_add.3 | 4 ++ include/seccomp.h.in | 10 ++++- src/gen_pfc.c | 7 +++- src/python/libseccomp.pxd | 1 + src/python/seccomp.pyx | 10 +++-- src/system.c | 21 +++++++++- src/system.h | 5 ++- tests/.gitignore | 1 + tests/38-basic-pfc_coverage.c | 3 ++ tests/38-basic-pfc_coverage.pfc | 6 +++ tests/43-sim-kill_process.c | 69 +++++++++++++++++++++++++++++++++ tests/43-sim-kill_process.py | 45 +++++++++++++++++++++ tests/43-sim-kill_process.tests | 19 +++++++++ tests/Makefile.am | 9 +++-- tools/bpf.h | 5 ++- tools/scmp_bpf_disasm.c | 7 +++- tools/scmp_bpf_sim.c | 7 +++- 18 files changed, 217 insertions(+), 16 deletions(-) create mode 100644 tests/43-sim-kill_process.c create mode 100755 tests/43-sim-kill_process.py create mode 100644 tests/43-sim-kill_process.tests diff --git a/doc/man/man3/seccomp_init.3 b/doc/man/man3/seccomp_init.3 index d7cd383a..ad1371f1 100644 --- a/doc/man/man3/seccomp_init.3 +++ b/doc/man/man3/seccomp_init.3 @@ -52,6 +52,10 @@ The thread will be terminated by the kernel with SIGSYS when it calls a syscall that does not match any of the configured seccomp filter rules. The thread will not be able to catch the signal. .TP +.B SCMP_ACT_KILL_PROCESS +The entire process will be terminated by the kernel with SIGSYS when it calls a +syscall that does not match any of the configured seccomp filter rules. +.TP .B SCMP_ACT_TRAP The thread will be sent a SIGSYS signal when it calls a syscall that does not match any of the configured seccomp filter rules. It may catch this and change diff --git a/doc/man/man3/seccomp_rule_add.3 b/doc/man/man3/seccomp_rule_add.3 index 86c53b19..b051577f 100644 --- a/doc/man/man3/seccomp_rule_add.3 +++ b/doc/man/man3/seccomp_rule_add.3 @@ -111,6 +111,10 @@ values are as follows: The thread will be killed by the kernel when it calls a syscall that matches the filter rule. .TP +.B SCMP_ACT_KILL_PROCESS +The process will be killed by the kernel when it calls a syscall that matches +the filter rule. +.TP .B SCMP_ACT_TRAP The thread will throw a SIGSYS signal when it calls a syscall that matches the filter rule. diff --git a/include/seccomp.h.in b/include/seccomp.h.in index 2a789d67..53fe959a 100644 --- a/include/seccomp.h.in +++ b/include/seccomp.h.in @@ -244,7 +244,15 @@ struct scmp_arg_cmp { /** * Kill the process */ -#define SCMP_ACT_KILL 0x00000000U +#define SCMP_ACT_KILL_PROCESS 0x80000000U +/** + * Kill the thread + */ +#define SCMP_ACT_KILL_THREAD 0x00000000U +/** + * Kill the thread. Defined for backward compatibility + */ +#define SCMP_ACT_KILL SCMP_ACT_KILL_THREAD /** * Throw a SIGSYS signal */ diff --git a/src/gen_pfc.c b/src/gen_pfc.c index 196635f9..eb1cb470 100644 --- a/src/gen_pfc.c +++ b/src/gen_pfc.c @@ -117,8 +117,11 @@ static void _pfc_arg(FILE *fds, */ static void _pfc_action(FILE *fds, uint32_t action) { - switch (action & 0xffff0000) { - case SCMP_ACT_KILL: + switch (action & SECCOMP_RET_ACTION_FULL) { + case SCMP_ACT_KILL_PROCESS: + fprintf(fds, "action KILL-PROCESS;\n"); + break; + case SCMP_ACT_KILL_THREAD: fprintf(fds, "action KILL;\n"); break; case SCMP_ACT_TRAP: diff --git a/src/python/libseccomp.pxd b/src/python/libseccomp.pxd index a599ef2e..49d0be4c 100644 --- a/src/python/libseccomp.pxd +++ b/src/python/libseccomp.pxd @@ -69,6 +69,7 @@ cdef extern from "seccomp.h": SCMP_CMP_MASKED_EQ cdef enum: + SCMP_ACT_KILL_PROCESS SCMP_ACT_KILL SCMP_ACT_TRAP SCMP_ACT_LOG diff --git a/src/python/seccomp.pyx b/src/python/seccomp.pyx index 159cbc18..bb5e01b5 100644 --- a/src/python/seccomp.pyx +++ b/src/python/seccomp.pyx @@ -29,7 +29,8 @@ based filtering interface that should be familiar to, and easily adopted by application developers. Filter action values: - KILL - kill the process + KILL_PROCESS - kill the process + KILL - kill the thread LOG - allow the syscall to be executed after the action has been logged ALLOW - allow the syscall to execute TRAP - a SIGSYS signal will be thrown @@ -94,6 +95,7 @@ def c_str(string): else: return bytes(string, "ascii") +KILL_PROCESS = libseccomp.SCMP_ACT_KILL_PROCESS KILL = libseccomp.SCMP_ACT_KILL TRAP = libseccomp.SCMP_ACT_TRAP LOG = libseccomp.SCMP_ACT_LOG @@ -545,7 +547,8 @@ cdef class SyscallFilter: """ Add a new rule to filter. Arguments: - action - the rule action: KILL, TRAP, ERRNO(), TRACE(), LOG, or ALLOW + action - the rule action: KILL_PROCESS, KILL, TRAP, ERRNO(), TRACE(), + LOG, or ALLOW syscall - the syscall name or number args - variable number of Arg objects @@ -627,7 +630,8 @@ cdef class SyscallFilter: """ Add a new rule to filter. Arguments: - action - the rule action: KILL, TRAP, ERRNO(), TRACE(), LOG, or ALLOW + action - the rule action: KILL_PROCESS, KILL, TRAP, ERRNO(), TRACE(), + LOG, or ALLOW syscall - the syscall name or number args - variable number of Arg objects diff --git a/src/system.c b/src/system.c index af461098..8ce4f445 100644 --- a/src/system.c +++ b/src/system.c @@ -123,7 +123,26 @@ void sys_set_seccomp_syscall(bool enable) */ int sys_chk_seccomp_action(uint32_t action) { - if (action == SCMP_ACT_KILL) { + int rc, nr_seccomp; + uint32_t kaction = action & SECCOMP_RET_ACTION_FULL; + + + nr_seccomp = arch_syscall_resolve_name(arch_def_native, "seccomp"); + if (nr_seccomp != __NR_SCMP_ERROR) { + rc = syscall(nr_seccomp, SECCOMP_GET_ACTION_AVAIL, 0, + &kaction); + if (rc == -EPERM) + /* valgrind currently doesn't support the seccomp() + * syscall. continue processing + */ + rc = 0; + else if (rc < 0) + return 0; + } + + if (action == SCMP_ACT_KILL_PROCESS) { + return 1; + } else if (action == SCMP_ACT_KILL_THREAD) { return 1; } else if (action == SCMP_ACT_TRAP) { return 1; diff --git a/src/system.h b/src/system.h index b7936877..51ad95f2 100644 --- a/src/system.h +++ b/src/system.h @@ -55,13 +55,16 @@ struct db_filter_col; * The ordering ensures that a min_t() over composed return values always * selects the least permissive choice. */ -#define SECCOMP_RET_KILL 0x00000000U /* kill the task immediately */ +#define SECCOMP_RET_KILL_PROCESS 0x80000000U /* kill the process immediately */ +#define SECCOMP_RET_KILL_THREAD 0x00000000U /* kill the thread immediately */ +#define SECCOMP_RET_KILL SECCOMP_RET_KILL_THREAD /* default to killing the thread */ #define SECCOMP_RET_TRAP 0x00030000U /* disallow and force a SIGSYS */ #define SECCOMP_RET_ERRNO 0x00050000U /* returns an errno */ #define SECCOMP_RET_TRACE 0x7ff00000U /* pass to a tracer or disallow */ #define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */ /* Masks for the return value sections. */ +#define SECCOMP_RET_ACTION_FULL 0xffff0000U #define SECCOMP_RET_ACTION 0x7fff0000U #define SECCOMP_RET_DATA 0x0000ffffU diff --git a/tests/.gitignore b/tests/.gitignore index c4f2bf8e..da3a86df 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -48,3 +48,4 @@ util.pyc 40-sim-log 41-sim-syscall_priority_arch 42-sim-adv_chains +43-sim-kill_process diff --git a/tests/38-basic-pfc_coverage.c b/tests/38-basic-pfc_coverage.c index a12d06c8..40025ef3 100644 --- a/tests/38-basic-pfc_coverage.c +++ b/tests/38-basic-pfc_coverage.c @@ -78,6 +78,9 @@ int main(int argc, char *argv[]) if (rc < 0) goto out; rc = seccomp_rule_add(ctx, SCMP_ACT_TRACE(1), SCMP_SYS(exit), 0); + if (rc < 0) + goto out; + rc = seccomp_rule_add(ctx, SCMP_ACT_KILL_PROCESS, SCMP_SYS(fstat), 0); if (rc < 0) goto out; diff --git a/tests/38-basic-pfc_coverage.pfc b/tests/38-basic-pfc_coverage.pfc index a0c31ac1..6c862baa 100644 --- a/tests/38-basic-pfc_coverage.pfc +++ b/tests/38-basic-pfc_coverage.pfc @@ -6,6 +6,9 @@ if ($arch == 3221225534) # filter for syscall "exit" (60) [priority: 65535] if ($syscall == 60) action TRACE(1); + # filter for syscall "fstat" (5) [priority: 65535] + if ($syscall == 5) + action KILL-PROCESS; # filter for syscall "close" (3) [priority: 65535] if ($syscall == 3) action ERRNO(1); @@ -65,6 +68,9 @@ if ($arch == 3221225534) action ALLOW; # filter for arch x86 (1073741827) if ($arch == 1073741827) + # filter for syscall "fstat" (108) [priority: 65535] + if ($syscall == 108) + action KILL-PROCESS; # filter for syscall "close" (6) [priority: 65535] if ($syscall == 6) action ERRNO(1); diff --git a/tests/43-sim-kill_process.c b/tests/43-sim-kill_process.c new file mode 100644 index 00000000..9647bcb6 --- /dev/null +++ b/tests/43-sim-kill_process.c @@ -0,0 +1,69 @@ +/** + * Seccomp Library test program + * + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * Author: Tom Hromatka + */ + +/* + * This library is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License as + * published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, see . + */ + +#include +#include + +#include + +#include "util.h" + +int main(int argc, char *argv[]) +{ + int rc; + struct util_options opts; + scmp_filter_ctx ctx = NULL; + + rc = util_getopt(argc, argv, &opts); + if (rc < 0) + goto out; + + ctx = seccomp_init(SCMP_ACT_KILL_PROCESS); + if (ctx == NULL) + return ENOMEM; + + rc = seccomp_arch_remove(ctx, SCMP_ARCH_NATIVE); + if (rc != 0) + goto out; + rc = seccomp_arch_add(ctx, SCMP_ARCH_X86_64); + if (rc != 0) + goto out; + + rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0); + if (rc != 0) + goto out; + + rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(5), SCMP_SYS(write), 0); + if (rc != 0) + goto out; + + rc = seccomp_rule_add(ctx, SCMP_ACT_KILL_THREAD, SCMP_SYS(open), 0); + if (rc != 0) + goto out; + + rc = util_filter_output(&opts, ctx); + if (rc) + goto out; + +out: + seccomp_release(ctx); + return (rc < 0 ? -rc : rc); +} diff --git a/tests/43-sim-kill_process.py b/tests/43-sim-kill_process.py new file mode 100755 index 00000000..092798a2 --- /dev/null +++ b/tests/43-sim-kill_process.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python + +# +# Seccomp Library test program +# +# Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. +# Author: Tom Hromatka +# + +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of version 2.1 of the GNU Lesser General Public License as +# published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, see . +# + +import argparse +import sys + +import util + +from seccomp import * + +def test(args): + f = SyscallFilter(KILL_PROCESS) + f.remove_arch(Arch()) + f.add_arch(Arch("x86_64")) + f.add_rule_exactly(ALLOW, "read") + f.add_rule_exactly(ERRNO(5), "write") + f.add_rule_exactly(KILL, "open") + return f + +args = util.get_opt() +ctx = test(args) +util.filter_output(args, ctx) + +# kate: syntax python; +# kate: indent-mode python; space-indent on; indent-width 4; mixedindent off; diff --git a/tests/43-sim-kill_process.tests b/tests/43-sim-kill_process.tests new file mode 100644 index 00000000..993bc2b9 --- /dev/null +++ b/tests/43-sim-kill_process.tests @@ -0,0 +1,19 @@ +# +# libseccomp regression test automation data +# +# Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. +# Author: Tom Hromatka +# + +test type: bpf-sim + +# Testname Arch Syscall Arg0 Arg1 Arg2 Arg3 Arg4 Arg5 Result +43-sim-kill_process +x86_64 0 N N N N N N ALLOW +43-sim-kill_process +x86_64 1 N N N N N N ERRNO(5) +43-sim-kill_process +x86_64 2 N N N N N N KILL +43-sim-kill_process +x86_64 3 N N N N N N KILL-PROCESS + +test type: bpf-valgrind + +# Testname +43-sim-kill_process diff --git a/tests/Makefile.am b/tests/Makefile.am index 3731c462..17870401 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -81,7 +81,8 @@ check_PROGRAMS = \ 39-basic-api_level \ 40-sim-log \ 41-sim-syscall_priority_arch \ - 42-sim-adv_chains + 42-sim-adv_chains \ + 43-sim-kill_process EXTRA_DIST_TESTPYTHON = \ util.py \ @@ -124,7 +125,8 @@ EXTRA_DIST_TESTPYTHON = \ 37-sim-ipc_syscalls_be.py \ 40-sim-log.py \ 41-sim-syscall_priority_arch.py \ - 42-sim-adv_chains.py + 42-sim-adv_chains.py \ + 43-sim-kill_process.py EXTRA_DIST_TESTCFGS = \ 01-sim-allow.tests \ @@ -168,7 +170,8 @@ EXTRA_DIST_TESTCFGS = \ 39-basic-api_level.tests \ 40-sim-log.tests \ 41-sim-syscall_priority_arch.tests \ - 42-sim-adv_chains.tests + 42-sim-adv_chains.tests \ + 43-sim-kill_process.tests EXTRA_DIST_TESTSCRIPTS = \ 38-basic-pfc_coverage.sh 38-basic-pfc_coverage.pfc diff --git a/tools/bpf.h b/tools/bpf.h index b8e6d818..fd204416 100644 --- a/tools/bpf.h +++ b/tools/bpf.h @@ -56,11 +56,14 @@ struct sock_filter { typedef struct sock_filter bpf_instr_raw; /* seccomp return masks */ +#define SECCOMP_RET_ACTION_FULL 0xffff0000U #define SECCOMP_RET_ACTION 0x7fff0000U #define SECCOMP_RET_DATA 0x0000ffffU /* seccomp action values */ -#define SECCOMP_RET_KILL 0x00000000U +#define SECCOMP_RET_KILL_PROCESS 0x80000000U +#define SECCOMP_RET_KILL_THREAD 0x00000000U +#define SECCOMP_RET_KILL SECCOMP_RET_KILL_THREAD #define SECCOMP_RET_TRAP 0x00030000U #define SECCOMP_RET_ERRNO 0x00050000U #define SECCOMP_RET_TRACE 0x7ff00000U diff --git a/tools/scmp_bpf_disasm.c b/tools/scmp_bpf_disasm.c index 6e5282a5..cb157daf 100644 --- a/tools/scmp_bpf_disasm.c +++ b/tools/scmp_bpf_disasm.c @@ -173,11 +173,14 @@ static const char *bpf_decode_op(const bpf_instr_raw *bpf) */ static void bpf_decode_action(uint32_t k) { - uint32_t act = k & SECCOMP_RET_ACTION; + uint32_t act = k & SECCOMP_RET_ACTION_FULL; uint32_t data = k & SECCOMP_RET_DATA; switch (act) { - case SECCOMP_RET_KILL: + case SECCOMP_RET_KILL_PROCESS: + printf("KILL-PROCESS"); + break; + case SECCOMP_RET_KILL_THREAD: printf("KILL"); break; case SECCOMP_RET_TRAP: diff --git a/tools/scmp_bpf_sim.c b/tools/scmp_bpf_sim.c index 6e422c5b..48f5b6c7 100644 --- a/tools/scmp_bpf_sim.c +++ b/tools/scmp_bpf_sim.c @@ -112,11 +112,14 @@ static void exit_error(unsigned int rc, unsigned int line) */ static void end_action(uint32_t action, unsigned int line) { - uint32_t act = action & SECCOMP_RET_ACTION; + uint32_t act = action & SECCOMP_RET_ACTION_FULL; uint32_t data = action & SECCOMP_RET_DATA; switch (act) { - case SECCOMP_RET_KILL: + case SECCOMP_RET_KILL_PROCESS: + fprintf(stdout, "KILL-PROCESS\n"); + break; + case SECCOMP_RET_KILL_THREAD: fprintf(stdout, "KILL\n"); break; case SECCOMP_RET_TRAP: From 2baa12f44addbc12b00bf786c0c5dade326283d3 Mon Sep 17 00:00:00 2001 From: Tom Hromatka Date: Tue, 20 Feb 2018 13:17:44 -0700 Subject: [PATCH 2/7] BUG: SECCOMP_RET_ACTION_FULL is undefined SECCOMP_RET_ACTION_FULL isn't available on kernels older than v4.14. Surround it with an #ifdef Signed-off-by: Tom Hromatka --- src/system.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/system.c b/src/system.c index 8ce4f445..eaea7af0 100644 --- a/src/system.c +++ b/src/system.c @@ -124,8 +124,10 @@ void sys_set_seccomp_syscall(bool enable) int sys_chk_seccomp_action(uint32_t action) { int rc, nr_seccomp; - uint32_t kaction = action & SECCOMP_RET_ACTION_FULL; +/* SECCOMP_RET_ACTION_FULL was defined in kernel v4.14 */ +#ifdef SECCOMP_RET_ACTION_FULL + uint32_t kaction = action & SECCOMP_RET_ACTION_FULL; nr_seccomp = arch_syscall_resolve_name(arch_def_native, "seccomp"); if (nr_seccomp != __NR_SCMP_ERROR) { @@ -139,6 +141,7 @@ int sys_chk_seccomp_action(uint32_t action) else if (rc < 0) return 0; } +#endif if (action == SCMP_ACT_KILL_PROCESS) { return 1; From a6bb0b810028bc2f55573f0c917748f9bbd70e22 Mon Sep 17 00:00:00 2001 From: Tom Hromatka Date: Tue, 20 Feb 2018 14:36:32 -0700 Subject: [PATCH 3/7] BUG: SECCOMP_RET_ACTION_FULL is undefined Signed-off-by: Tom Hromatka --- src/gen_pfc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/gen_pfc.c b/src/gen_pfc.c index eb1cb470..d5a53daf 100644 --- a/src/gen_pfc.c +++ b/src/gen_pfc.c @@ -110,6 +110,10 @@ static void _pfc_arg(FILE *fds, fprintf(fds, "$a%d", node->arg); } +#ifndef SECCOMP_RET_ACTION_FULL +#define SECCOMP_RET_ACTION_FULL 0xffff0000U +#endif + /** * Display a string representation of the filter action * @param fds the file stream to send the output From d9ccdcff48d3c155b1a13f81c2a5729b6e126b12 Mon Sep 17 00:00:00 2001 From: Tom Hromatka Date: Thu, 22 Feb 2018 14:36:55 -0700 Subject: [PATCH 4/7] api: addressed code review comments * Added an API level 4 to better support KILL_PROCESS in sys_chk_seccomp_action() * Added a live test for KILL_PROCESS * Other small cleanups Signed-off-by: Tom Hromatka --- include/seccomp.h.in | 1 + src/api.c | 10 +++ src/gen_pfc.c | 7 +-- src/system.c | 34 ++++------ tests/.gitignore | 1 + tests/06-sim-actions.c | 6 +- tests/06-sim-actions.py | 3 +- tests/06-sim-actions.tests | 3 +- tests/38-basic-pfc_coverage.c | 4 ++ tests/38-basic-pfc_coverage.pfc | 4 +- tests/43-sim-kill_process.c | 4 ++ tests/43-sim-kill_process.tests | 2 +- tests/44-live-kill_process.c | 105 +++++++++++++++++++++++++++++++ tests/44-live-kill_process.py | 68 ++++++++++++++++++++ tests/44-live-kill_process.tests | 11 ++++ tests/Makefile.am | 11 ++-- tests/regression | 7 ++- tests/util.c | 2 + tools/scmp_bpf_disasm.c | 2 +- tools/scmp_bpf_sim.c | 2 +- 20 files changed, 248 insertions(+), 39 deletions(-) create mode 100644 tests/44-live-kill_process.c create mode 100755 tests/44-live-kill_process.py create mode 100644 tests/44-live-kill_process.tests diff --git a/include/seccomp.h.in b/include/seccomp.h.in index 53fe959a..8e2d7f9b 100644 --- a/include/seccomp.h.in +++ b/include/seccomp.h.in @@ -305,6 +305,7 @@ const struct scmp_version *seccomp_version(void); * uses the seccomp(2) syscall instead of the prctl(2) syscall * 3 : support for the SCMP_FLTATR_CTL_LOG filter attribute * support for the SCMP_ACT_LOG action + * 4 : support for the SCMP_ACT_KILL_PROCESS action * */ const unsigned int seccomp_api_get(void); diff --git a/src/api.c b/src/api.c index e1a5e8b1..8198fd48 100644 --- a/src/api.c +++ b/src/api.c @@ -135,18 +135,28 @@ API int seccomp_api_set(unsigned int level) sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC, false); sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_LOG, false); sys_set_seccomp_action(SCMP_ACT_LOG, false); + sys_set_seccomp_action(SCMP_ACT_KILL_PROCESS, false); break; case 2: sys_set_seccomp_syscall(true); sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC, true); sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_LOG, false); sys_set_seccomp_action(SCMP_ACT_LOG, false); + sys_set_seccomp_action(SCMP_ACT_KILL_PROCESS, false); break; case 3: sys_set_seccomp_syscall(true); sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC, true); sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_LOG, true); sys_set_seccomp_action(SCMP_ACT_LOG, true); + sys_set_seccomp_action(SCMP_ACT_KILL_PROCESS, false); + break; + case 4: + sys_set_seccomp_syscall(true); + sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_TSYNC, true); + sys_set_seccomp_flag(SECCOMP_FILTER_FLAG_LOG, true); + sys_set_seccomp_action(SCMP_ACT_LOG, true); + sys_set_seccomp_action(SCMP_ACT_KILL_PROCESS, true); break; default: return -EINVAL; diff --git a/src/gen_pfc.c b/src/gen_pfc.c index d5a53daf..68051ce6 100644 --- a/src/gen_pfc.c +++ b/src/gen_pfc.c @@ -35,6 +35,7 @@ #include "db.h" #include "gen_pfc.h" #include "helper.h" +#include "system.h" struct pfc_sys_list { struct db_sys_list *sys; @@ -110,10 +111,6 @@ static void _pfc_arg(FILE *fds, fprintf(fds, "$a%d", node->arg); } -#ifndef SECCOMP_RET_ACTION_FULL -#define SECCOMP_RET_ACTION_FULL 0xffff0000U -#endif - /** * Display a string representation of the filter action * @param fds the file stream to send the output @@ -123,7 +120,7 @@ static void _pfc_action(FILE *fds, uint32_t action) { switch (action & SECCOMP_RET_ACTION_FULL) { case SCMP_ACT_KILL_PROCESS: - fprintf(fds, "action KILL-PROCESS;\n"); + fprintf(fds, "action KILL_PROCESS;\n"); break; case SCMP_ACT_KILL_THREAD: fprintf(fds, "action KILL;\n"); diff --git a/src/system.c b/src/system.c index eaea7af0..0501b76d 100644 --- a/src/system.c +++ b/src/system.c @@ -43,6 +43,7 @@ static int _support_seccomp_syscall = -1; static int _support_seccomp_flag_tsync = -1; static int _support_seccomp_flag_log = -1; static int _support_seccomp_action_log = -1; +static int _support_seccomp_kill_process = -1; /** * Check to see if the seccomp() syscall is supported @@ -123,28 +124,17 @@ void sys_set_seccomp_syscall(bool enable) */ int sys_chk_seccomp_action(uint32_t action) { - int rc, nr_seccomp; - -/* SECCOMP_RET_ACTION_FULL was defined in kernel v4.14 */ -#ifdef SECCOMP_RET_ACTION_FULL - uint32_t kaction = action & SECCOMP_RET_ACTION_FULL; - - nr_seccomp = arch_syscall_resolve_name(arch_def_native, "seccomp"); - if (nr_seccomp != __NR_SCMP_ERROR) { - rc = syscall(nr_seccomp, SECCOMP_GET_ACTION_AVAIL, 0, - &kaction); - if (rc == -EPERM) - /* valgrind currently doesn't support the seccomp() - * syscall. continue processing - */ - rc = 0; - else if (rc < 0) - return 0; - } -#endif - if (action == SCMP_ACT_KILL_PROCESS) { - return 1; + if (_support_seccomp_kill_process < 0) { + if (sys_chk_seccomp_syscall() == 1 && + syscall(_nr_seccomp, SECCOMP_GET_ACTION_AVAIL, 0, + &action) == 0) + _support_seccomp_kill_process = 1; + else + _support_seccomp_kill_process = 0; + } + + return _support_seccomp_kill_process; } else if (action == SCMP_ACT_KILL_THREAD) { return 1; } else if (action == SCMP_ACT_TRAP) { @@ -184,6 +174,8 @@ void sys_set_seccomp_action(uint32_t action, bool enable) { if (action == SCMP_ACT_LOG) _support_seccomp_action_log = (enable ? 1 : 0); + else if (action == SCMP_ACT_KILL_PROCESS) + _support_seccomp_kill_process = (enable ? 1 : 0); } /** diff --git a/tests/.gitignore b/tests/.gitignore index da3a86df..568e9df5 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -49,3 +49,4 @@ util.pyc 41-sim-syscall_priority_arch 42-sim-adv_chains 43-sim-kill_process +44-live-kill_process diff --git a/tests/06-sim-actions.c b/tests/06-sim-actions.c index d81e5211..04968a9b 100644 --- a/tests/06-sim-actions.c +++ b/tests/06-sim-actions.c @@ -36,7 +36,7 @@ int main(int argc, char *argv[]) if (rc < 0) goto out; - rc = seccomp_api_set(3); + rc = seccomp_api_set(4); if (rc != 0) return EOPNOTSUPP; @@ -64,6 +64,10 @@ int main(int argc, char *argv[]) if (rc != 0) goto out; + rc = seccomp_rule_add(ctx, SCMP_ACT_KILL_PROCESS, SCMP_SYS(stat), 0); + if (rc != 0) + goto out; + rc = util_filter_output(&opts, ctx); if (rc) goto out; diff --git a/tests/06-sim-actions.py b/tests/06-sim-actions.py index e3f91e98..f0fe6f47 100755 --- a/tests/06-sim-actions.py +++ b/tests/06-sim-actions.py @@ -30,7 +30,7 @@ from seccomp import * def test(args): - set_api(3) + set_api(4) f = SyscallFilter(KILL) f.add_rule(ALLOW, "read") @@ -38,6 +38,7 @@ def test(args): f.add_rule(ERRNO(errno.EPERM), "write") f.add_rule(TRAP, "close") f.add_rule(TRACE(1234), "open") + f.add_rule(KILL_PROCESS, "stat") return f args = util.get_opt() diff --git a/tests/06-sim-actions.tests b/tests/06-sim-actions.tests index 1402e213..40a93aea 100644 --- a/tests/06-sim-actions.tests +++ b/tests/06-sim-actions.tests @@ -12,11 +12,12 @@ test type: bpf-sim 06-sim-actions all write 1 0x856B008 N N N N ERRNO(1) 06-sim-actions all close 4 N N N N N TRAP 06-sim-actions all,-aarch64 open 0x856B008 4 N N N N TRACE(1234) +06-sim-actions all stat N N N N N N KILL_PROCESS 06-sim-actions all rt_sigreturn N N N N N N LOG 06-sim-actions x86 0-2 N N N N N N KILL 06-sim-actions x86 7-172 N N N N N N KILL 06-sim-actions x86 174-350 N N N N N N KILL -06-sim-actions x86_64 4-14 N N N N N N KILL +06-sim-actions x86_64 5-14 N N N N N N KILL 06-sim-actions x86_64 16-350 N N N N N N KILL test type: bpf-sim-fuzz diff --git a/tests/38-basic-pfc_coverage.c b/tests/38-basic-pfc_coverage.c index 40025ef3..a53ee215 100644 --- a/tests/38-basic-pfc_coverage.c +++ b/tests/38-basic-pfc_coverage.c @@ -38,6 +38,10 @@ int main(int argc, char *argv[]) /* stdout */ fd = 1; + rc = seccomp_api_set(4); + if (rc != 0) + return EOPNOTSUPP; + ctx = seccomp_init(SCMP_ACT_ALLOW); if (ctx == NULL) { rc = ENOMEM; diff --git a/tests/38-basic-pfc_coverage.pfc b/tests/38-basic-pfc_coverage.pfc index 6c862baa..a9a7019f 100644 --- a/tests/38-basic-pfc_coverage.pfc +++ b/tests/38-basic-pfc_coverage.pfc @@ -8,7 +8,7 @@ if ($arch == 3221225534) action TRACE(1); # filter for syscall "fstat" (5) [priority: 65535] if ($syscall == 5) - action KILL-PROCESS; + action KILL_PROCESS; # filter for syscall "close" (3) [priority: 65535] if ($syscall == 3) action ERRNO(1); @@ -70,7 +70,7 @@ if ($arch == 3221225534) if ($arch == 1073741827) # filter for syscall "fstat" (108) [priority: 65535] if ($syscall == 108) - action KILL-PROCESS; + action KILL_PROCESS; # filter for syscall "close" (6) [priority: 65535] if ($syscall == 6) action ERRNO(1); diff --git a/tests/43-sim-kill_process.c b/tests/43-sim-kill_process.c index 9647bcb6..7fd08e14 100644 --- a/tests/43-sim-kill_process.c +++ b/tests/43-sim-kill_process.c @@ -36,6 +36,10 @@ int main(int argc, char *argv[]) if (rc < 0) goto out; + rc = seccomp_api_set(4); + if (rc != 0) + return EOPNOTSUPP; + ctx = seccomp_init(SCMP_ACT_KILL_PROCESS); if (ctx == NULL) return ENOMEM; diff --git a/tests/43-sim-kill_process.tests b/tests/43-sim-kill_process.tests index 993bc2b9..f355be66 100644 --- a/tests/43-sim-kill_process.tests +++ b/tests/43-sim-kill_process.tests @@ -11,7 +11,7 @@ test type: bpf-sim 43-sim-kill_process +x86_64 0 N N N N N N ALLOW 43-sim-kill_process +x86_64 1 N N N N N N ERRNO(5) 43-sim-kill_process +x86_64 2 N N N N N N KILL -43-sim-kill_process +x86_64 3 N N N N N N KILL-PROCESS +43-sim-kill_process +x86_64 3 N N N N N N KILL_PROCESS test type: bpf-valgrind diff --git a/tests/44-live-kill_process.c b/tests/44-live-kill_process.c new file mode 100644 index 00000000..03118557 --- /dev/null +++ b/tests/44-live-kill_process.c @@ -0,0 +1,105 @@ +/** + * Seccomp Library test program + * + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * Author: Tom Hromatka + */ + +/* + * This library is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License as + * published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "util.h" + + +static const unsigned int whitelist[] = { + SCMP_SYS(clone), + SCMP_SYS(exit), + SCMP_SYS(exit_group), + SCMP_SYS(futex), + SCMP_SYS(madvise), + SCMP_SYS(mmap), + SCMP_SYS(mprotect), + SCMP_SYS(munmap), + SCMP_SYS(nanosleep), + SCMP_SYS(set_robust_list), +}; + +/** + * Child thread created via pthread_create() + * + * This thread will call a disallowed syscall. It should + * cause the entire program to die (and not just this + * thread.) + */ +void *child_start(void *param) +{ + int fd, *i = (int *)param; + + *i = 1; + + /* make a disallowed syscall */ + fd = open("/dev/null", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); + /* we should never get here. seccomp should kill the entire + * process when open() is called. + */ + if (fd < 0) + *i = fd; + + return NULL; +} + +int main(int argc, char *argv[]) +{ + int rc, i, param = 0; + scmp_filter_ctx ctx = NULL; + pthread_t child_thread; + + ctx = seccomp_init(SCMP_ACT_KILL_PROCESS); + if (ctx == NULL) + return ENOMEM; + + for (i = 0; i < sizeof(whitelist) / sizeof(whitelist[0]); i++) { + rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, whitelist[i], 0); + if (rc != 0) + goto out; + } + + rc = seccomp_load(ctx); + if (rc != 0) + goto out; + + rc = pthread_create(&child_thread, NULL, child_start, ¶m); + if (rc != 0) + goto out; + + /* sleep for a bit to ensure that the child thread has time to run */ + sleep(1); + + /* we should never get here! */ + rc = -EACCES; + goto out; + +out: + seccomp_release(ctx); + return (rc < 0 ? -rc : rc); +} diff --git a/tests/44-live-kill_process.py b/tests/44-live-kill_process.py new file mode 100755 index 00000000..30507420 --- /dev/null +++ b/tests/44-live-kill_process.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python + +# +# Seccomp Library test program +# +# Copyright (c) 2013 Red Hat +# Author: Paul Moore +# + +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of version 2.1 of the GNU Lesser General Public License as +# published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, see . +# + +import argparse +import os +import sys +import threading +import time + +import util + +from seccomp import * + +def child_start(param): + param = 1 + + try: + fd = os.open("/dev/null", os.O_WRONLY) + except IOError as ex: + param = ex.errno + quit(ex.errno) + +def test(): + f = SyscallFilter(KILL_PROCESS) + f.add_rule(ALLOW, "clone") + f.add_rule(ALLOW, "exit") + f.add_rule(ALLOW, "exit_group") + f.add_rule(ALLOW, "futex") + f.add_rule(ALLOW, "madvise") + f.add_rule(ALLOW, "mmap") + f.add_rule(ALLOW, "mprotect") + f.add_rule(ALLOW, "munmap") + f.add_rule(ALLOW, "nanosleep") + f.add_rule(ALLOW, "set_robust_list") + f.load() + + param = 0 + threading.Thread(target = child_start, args = (param, )) + thread.start() + + time.sleep(1) + + quit(-errno.EACCES) + +test() + +# kate: syntax python; +# kate: indent-mode python; space-indent on; indent-width 4; mixedindent off; diff --git a/tests/44-live-kill_process.tests b/tests/44-live-kill_process.tests new file mode 100644 index 00000000..ccbecb94 --- /dev/null +++ b/tests/44-live-kill_process.tests @@ -0,0 +1,11 @@ +# +# libseccomp regression test automation data +# +# Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. +# Author: Tom Hromatka +# + +test type: live + +# Testname Result +44-live-kill_process KILL_PROCESS diff --git a/tests/Makefile.am b/tests/Makefile.am index 17870401..3a1015ea 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -26,7 +26,7 @@ else DBG_STATIC = -static endif -AM_LDFLAGS = ${DBG_STATIC} +AM_LDFLAGS = ${DBG_STATIC} -lpthread LDADD = util.la ../src/libseccomp.la ${CODE_COVERAGE_LIBS} @@ -82,7 +82,8 @@ check_PROGRAMS = \ 40-sim-log \ 41-sim-syscall_priority_arch \ 42-sim-adv_chains \ - 43-sim-kill_process + 43-sim-kill_process \ + 44-live-kill_process EXTRA_DIST_TESTPYTHON = \ util.py \ @@ -126,7 +127,8 @@ EXTRA_DIST_TESTPYTHON = \ 40-sim-log.py \ 41-sim-syscall_priority_arch.py \ 42-sim-adv_chains.py \ - 43-sim-kill_process.py + 43-sim-kill_process.py \ + 44-live-kill_process.py EXTRA_DIST_TESTCFGS = \ 01-sim-allow.tests \ @@ -171,7 +173,8 @@ EXTRA_DIST_TESTCFGS = \ 40-sim-log.tests \ 41-sim-syscall_priority_arch.tests \ 42-sim-adv_chains.tests \ - 43-sim-kill_process.tests + 43-sim-kill_process.tests \ + 44-live-kill_process.tests EXTRA_DIST_TESTSCRIPTS = \ 38-basic-pfc_coverage.sh 38-basic-pfc_coverage.pfc diff --git a/tests/regression b/tests/regression index 2a7a5024..164273f5 100755 --- a/tests/regression +++ b/tests/regression @@ -724,6 +724,7 @@ function run_test_live() { # setup the arch specific return values case "$arch" in x86|x86_64|x32|arm|aarch64|parisc|parisc64|ppc|ppc64|ppc64le|ppc|s390|s390x) + rc_kill_process=159 rc_kill=159 rc_allow=160 rc_trap=161 @@ -732,6 +733,7 @@ function run_test_live() { rc_log=164 ;; mips|mipsel|mips64|mips64n32|mipsel64|mipsel64n32) + rc_kill_process=140 rc_kill=140 rc_allow=160 rc_trap=161 @@ -747,7 +749,10 @@ function run_test_live() { esac # verify the results - if [[ $line_act == "KILL" && $rc -eq $rc_kill ]]; then + if [[ $line_act == "KILL_PROCESS" && $rc -eq $rc_kill_process ]]; then + print_result $1 "SUCCESS" "" + stats_success=$(($stats_success+1)) + elif [[ $line_act == "KILL" && $rc -eq $rc_kill ]]; then print_result $1 "SUCCESS" "" stats_success=$(($stats_success+1)) elif [[ $line_act == "ALLOW" && $rc -eq $rc_allow ]]; then diff --git a/tests/util.c b/tests/util.c index f079a53f..a84e475d 100644 --- a/tests/util.c +++ b/tests/util.c @@ -168,6 +168,8 @@ int util_action_parse(const char *action) if (strcasecmp(action, "KILL") == 0) return SCMP_ACT_KILL; + if (strcasecmp(action, "KILL_PROCESS") == 0) + return SCMP_ACT_KILL_PROCESS; else if (strcasecmp(action, "TRAP") == 0) return SCMP_ACT_TRAP; else if (strcasecmp(action, "ERRNO") == 0) diff --git a/tools/scmp_bpf_disasm.c b/tools/scmp_bpf_disasm.c index cb157daf..27fba9ad 100644 --- a/tools/scmp_bpf_disasm.c +++ b/tools/scmp_bpf_disasm.c @@ -178,7 +178,7 @@ static void bpf_decode_action(uint32_t k) switch (act) { case SECCOMP_RET_KILL_PROCESS: - printf("KILL-PROCESS"); + printf("KILL_PROCESS"); break; case SECCOMP_RET_KILL_THREAD: printf("KILL"); diff --git a/tools/scmp_bpf_sim.c b/tools/scmp_bpf_sim.c index 48f5b6c7..73d056b1 100644 --- a/tools/scmp_bpf_sim.c +++ b/tools/scmp_bpf_sim.c @@ -117,7 +117,7 @@ static void end_action(uint32_t action, unsigned int line) switch (act) { case SECCOMP_RET_KILL_PROCESS: - fprintf(stdout, "KILL-PROCESS\n"); + fprintf(stdout, "KILL_PROCESS\n"); break; case SECCOMP_RET_KILL_THREAD: fprintf(stdout, "KILL\n"); From c41f381719c6253543526f6a8357ecf7284f03aa Mon Sep 17 00:00:00 2001 From: Tom Hromatka Date: Thu, 22 Feb 2018 14:47:45 -0700 Subject: [PATCH 5/7] BUG: SECCOMP_RET_ACTION_FULL may be undefined Signed-off-by: Tom Hromatka --- src/system.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/system.h b/src/system.h index 51ad95f2..a8ff4b0d 100644 --- a/src/system.h +++ b/src/system.h @@ -37,6 +37,13 @@ struct db_filter_col; /* system header file */ #include +/* SECCOMP_RET_ACTION_FULL was added in kernel v4.14. It may not be + * defined on older kernels + */ +#ifndef SECCOMP_RET_ACTION_FULL +#define SECCOMP_RET_ACTION_FULL 0xffff0000U +#endif + #else /* NOTE: the definitions below were taken from the Linux Kernel sources */ From bbbc730d65b699202c5347068733ca612a7b9082 Mon Sep 17 00:00:00 2001 From: Tom Hromatka Date: Thu, 22 Feb 2018 16:08:19 -0700 Subject: [PATCH 6/7] BUG: improve code coverage Signed-off-by: Tom Hromatka --- tests/43-sim-kill_process.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/43-sim-kill_process.c b/tests/43-sim-kill_process.c index 7fd08e14..9647bcb6 100644 --- a/tests/43-sim-kill_process.c +++ b/tests/43-sim-kill_process.c @@ -36,10 +36,6 @@ int main(int argc, char *argv[]) if (rc < 0) goto out; - rc = seccomp_api_set(4); - if (rc != 0) - return EOPNOTSUPP; - ctx = seccomp_init(SCMP_ACT_KILL_PROCESS); if (ctx == NULL) return ENOMEM; From 778e19936ff913d95c3e4aec31c893a9c8d4a57e Mon Sep 17 00:00:00 2001 From: Tom Hromatka Date: Thu, 22 Feb 2018 16:59:03 -0700 Subject: [PATCH 7/7] BUG: disable the valgrind kill process test Signed-off-by: Tom Hromatka --- tests/43-sim-kill_process.tests | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/43-sim-kill_process.tests b/tests/43-sim-kill_process.tests index f355be66..77a84fde 100644 --- a/tests/43-sim-kill_process.tests +++ b/tests/43-sim-kill_process.tests @@ -13,7 +13,5 @@ test type: bpf-sim 43-sim-kill_process +x86_64 2 N N N N N N KILL 43-sim-kill_process +x86_64 3 N N N N N N KILL_PROCESS -test type: bpf-valgrind - # Testname 43-sim-kill_process