aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJorge Lucangeli Obes <jorgelo@google.com>2019-06-12 14:45:06 -0400
committerJorge Lucangeli Obes <jorgelo@google.com>2019-06-24 10:02:41 -0400
commit32201f8a02ad582aa08b8a71ff2938dc7bc038d1 (patch)
treeca5061c1290ac2d7df69c09959d70040cbcc8e01
parente1a868923c2daf9a7ed3b9300f5e18ca295bf0e8 (diff)
downloadminijail-32201f8a02ad582aa08b8a71ff2938dc7bc038d1.tar.gz
Add support for SECCOMP_RET_LOG.
Detect at runtime whether SECCOMP_RET_LOG is available and use that for logging. Bug: chromium:934859 Test: New unit tests. Test: On 4.14 device, minijail0 -S -L test/seccomp.policy -- /bin/true. Test: audit.log shows failing syscall, binary exits successfully. Test: On <4.14 device, behaves as before. Change-Id: Ic9da1b5dae2b4b1df50e9d3e6f18c816e93bff87
-rw-r--r--libminijail.c136
-rw-r--r--minijail0.112
-rw-r--r--minijail0_cli.c5
-rw-r--r--parse_seccomp_policy.cc8
-rw-r--r--syscall_filter.c2
-rw-r--r--syscall_filter_unittest.cc192
-rw-r--r--syscall_filter_unittest_macros.h14
-rw-r--r--system.c58
-rw-r--r--system.h3
-rw-r--r--system_unittest.cc5
-rw-r--r--util.h8
11 files changed, 366 insertions, 77 deletions
diff --git a/libminijail.c b/libminijail.c
index 9b54be3..6608cdd 100644
--- a/libminijail.c
+++ b/libminijail.c
@@ -382,6 +382,17 @@ void API minijail_set_seccomp_filter_tsync(struct minijail *j)
die("minijail_set_seccomp_filter_tsync() must be called "
"before minijail_parse_seccomp_filters()");
}
+
+ if (j->flags.seccomp_filter_logging && !seccomp_ret_log_available()) {
+ /*
+ * If SECCOMP_RET_LOG is not available, we don't want to use
+ * SECCOMP_RET_TRAP to both kill the entire process and report
+ * failing syscalls, since it will be brittle. Just bail.
+ */
+ die("SECCOMP_RET_LOG not available, cannot use logging and "
+ "thread sync at the same time");
+ }
+
j->flags.seccomp_filter_tsync = 1;
}
@@ -391,11 +402,23 @@ void API minijail_log_seccomp_filter_failures(struct minijail *j)
die("minijail_log_seccomp_filter_failures() must be called "
"before minijail_parse_seccomp_filters()");
}
-#ifdef ALLOW_DEBUG_LOGGING
- j->flags.seccomp_filter_logging = 1;
-#else
- warn("non-debug build: ignoring request to enable seccomp logging");
-#endif
+
+ if (j->flags.seccomp_filter_tsync && !seccomp_ret_log_available()) {
+ /*
+ * If SECCOMP_RET_LOG is not available, we don't want to use
+ * SECCOMP_RET_TRAP to both kill the entire process and report
+ * failing syscalls, since it will be brittle. Just bail.
+ */
+ die("SECCOMP_RET_LOG not available, cannot use logging and "
+ "thread sync at the same time");
+ }
+
+ if (debug_logging_allowed()) {
+ j->flags.seccomp_filter_logging = 1;
+ } else {
+ warn("non-debug build: ignoring request to enable seccomp "
+ "logging");
+ }
}
void API minijail_use_caps(struct minijail *j, uint64_t capmask)
@@ -932,12 +955,17 @@ static int seccomp_should_use_filters(struct minijail *j)
}
static int set_seccomp_filters_internal(struct minijail *j,
- struct sock_fprog *filter, bool owned)
+ const struct sock_fprog *filter,
+ bool owned)
{
struct sock_fprog *fprog;
if (owned) {
- fprog = filter;
+ /*
+ * If |owned| is true, it's OK to cast away the const-ness since
+ * we'll own the pointer going forward.
+ */
+ fprog = (struct sock_fprog *)filter;
} else {
fprog = malloc(sizeof(struct sock_fprog));
if (!fprog)
@@ -962,48 +990,48 @@ static int set_seccomp_filters_internal(struct minijail *j,
return 0;
}
-void API minijail_set_seccomp_filters(struct minijail *j,
- const struct sock_fprog *filter)
+static int parse_seccomp_filters(struct minijail *j, const char *filename,
+ FILE *policy_file)
{
- if (!seccomp_should_use_filters(j))
- return;
+ struct sock_fprog *fprog = malloc(sizeof(struct sock_fprog));
+ if (!fprog)
+ return -ENOMEM;
- if (j->flags.seccomp_filter_logging) {
- die("minijail_log_seccomp_filter_failures() is incompatible "
- "with minijail_set_seccomp_filters()");
- }
+ struct filter_options filteropts;
/*
- * set_seccomp_filters_internal() can only fail with ENOMEM.
- * Furthermore, since we won't own the incoming filter, it will not be
- * modified.
+ * Figure out filter options.
+ * Allow logging?
*/
- if (set_seccomp_filters_internal(j, (struct sock_fprog *)filter,
- false) < 0) {
- die("failed to copy seccomp filter");
+ filteropts.allow_logging =
+ debug_logging_allowed() && j->flags.seccomp_filter_logging;
+
+ /* What to do on a blocked system call? */
+ if (filteropts.allow_logging) {
+ if (seccomp_ret_log_available())
+ filteropts.action = ACTION_RET_LOG;
+ else
+ filteropts.action = ACTION_RET_TRAP;
+ } else {
+ if (j->flags.seccomp_filter_tsync)
+ filteropts.action = ACTION_RET_TRAP;
+ else
+ filteropts.action = ACTION_RET_KILL;
}
-}
-static int parse_seccomp_filters(struct minijail *j, const char *filename,
- FILE *policy_file)
-{
- struct sock_fprog *fprog = malloc(sizeof(struct sock_fprog));
- if (!fprog)
- return -ENOMEM;
- int use_ret_trap =
- j->flags.seccomp_filter_tsync || j->flags.seccomp_filter_logging;
+ /*
+ * If SECCOMP_RET_LOG is not available, need to allow extra syscalls
+ * for logging.
+ */
+ filteropts.allow_syscalls_for_logging =
+ filteropts.allow_logging && !seccomp_ret_log_available();
- struct filter_options filteropts = {
- .action = use_ret_trap ? ACTION_RET_TRAP : ACTION_RET_KILL,
- .allow_logging = j->flags.seccomp_filter_logging,
- .allow_syscalls_for_logging = j->flags.seccomp_filter_logging,
- };
if (compile_filter(filename, policy_file, fprog, &filteropts)) {
free(fprog);
return -1;
}
- return set_seccomp_filters_internal(j, fprog, true);
+ return set_seccomp_filters_internal(j, fprog, true /* owned */);
}
void API minijail_parse_seccomp_filters(struct minijail *j, const char *path)
@@ -1051,6 +1079,27 @@ void API minijail_parse_seccomp_filters_from_fd(struct minijail *j, int fd)
fclose(file);
}
+void API minijail_set_seccomp_filters(struct minijail *j,
+ const struct sock_fprog *filter)
+{
+ if (!seccomp_should_use_filters(j))
+ return;
+
+ if (j->flags.seccomp_filter_logging) {
+ die("minijail_log_seccomp_filter_failures() is incompatible "
+ "with minijail_set_seccomp_filters()");
+ }
+
+ /*
+ * set_seccomp_filters_internal() can only fail with ENOMEM.
+ * Furthermore, since we won't own the incoming filter, it will not be
+ * modified.
+ */
+ if (set_seccomp_filters_internal(j, filter, false /* owned */) < 0) {
+ die("failed to set seccomp filter");
+ }
+}
+
int API minijail_use_alt_syscall(struct minijail *j, const char *table)
{
j->alt_syscall_table = strdup(table);
@@ -1962,13 +2011,16 @@ static void set_seccomp_filter(const struct minijail *j)
if (j->flags.seccomp_filter) {
if (j->flags.seccomp_filter_logging) {
- /*
- * If logging seccomp filter failures,
- * install the SIGSYS handler first.
- */
- if (install_sigsys_handler())
- pdie("failed to install SIGSYS handler");
warn("logging seccomp filter failures");
+ if (!seccomp_ret_log_available()) {
+ /*
+ * If SECCOMP_RET_LOG is not available,
+ * install the SIGSYS handler first.
+ */
+ if (install_sigsys_handler())
+ pdie(
+ "failed to install SIGSYS handler");
+ }
} else if (j->flags.seccomp_filter_tsync) {
/*
* If setting thread sync,
diff --git a/minijail0.1 b/minijail0.1
index a18454e..d23f14a 100644
--- a/minijail0.1
+++ b/minijail0.1
@@ -157,9 +157,13 @@ Run inside a new IPC namespace. This option makes the program's System V IPC
namespace independent.
.TP
\fB-L\fR
-Report blocked syscalls to syslog when using seccomp filter. This option will
-force certain syscalls to be allowed in order to achieve this, depending on the
-system.
+Report blocked syscalls when using a seccomp filter. On kernels with support for
+SECCOMP_RET_LOG, every blocked syscall will be reported through the audit
+subsystem (see \fBseccomp\fR(2) for more details on SECCOMP_RET_LOG
+availability.) On all other kernels, the first failing syscall will be logged to
+syslog. This latter case will also force certain syscalls to be allowed in order
+to write to syslog. Note: this option is disabled and ignored for release
+builds.
.TP
\fB-m[<uid> <loweruid> <count>[,<uid> <loweruid> <count>]]\fR
Set the uid mapping of a user namespace (implies \fB-pU\fR). Same arguments as
@@ -345,4 +349,4 @@ The Chromium OS Authors <chromiumos-dev@chromium.org>
Copyright \(co 2011 The Chromium OS Authors
License BSD-like.
.SH "SEE ALSO"
-\fBlibminijail.h\fR \fBminijail0\fR(5)
+\fBlibminijail.h\fR \fBminijail0\fR(5) \fBseccomp\fR(2)
diff --git a/minijail0_cli.c b/minijail0_cli.c
index b0b6518..1b92b09 100644
--- a/minijail0_cli.c
+++ b/minijail0_cli.c
@@ -505,8 +505,9 @@ static void usage(const char *progn)
" -K: Do not change share mode of any existing mounts.\n"
" -K<mode>: Mark all existing mounts as <mode> instead of MS_PRIVATE.\n"
" -l: Enter new IPC namespace.\n"
- " -L: Report blocked syscalls to syslog when using seccomp filter.\n"
- " Forces the following syscalls to be allowed:\n"
+ " -L: Report blocked syscalls when using seccomp filter.\n"
+ " If the kernel does not support SECCOMP_RET_LOG,\n"
+ " forces the following syscalls to be allowed:\n"
" ", progn);
/* clang-format on */
for (i = 0; i < log_syscalls_len; i++)
diff --git a/parse_seccomp_policy.cc b/parse_seccomp_policy.cc
index c563a4a..38fcbee 100644
--- a/parse_seccomp_policy.cc
+++ b/parse_seccomp_policy.cc
@@ -78,8 +78,14 @@ int main(int argc, char** argv) {
if (!f)
pdie("fopen(%s) failed", argv[1]);
+ struct filter_options fopts {
+ .action = ACTION_RET_KILL,
+ .allow_logging = 0,
+ .allow_syscalls_for_logging = 0,
+ };
+
struct sock_fprog fp;
- int res = compile_filter(argv[1], f, &fp, 0, 0);
+ int res = compile_filter(argv[1], f, &fp, &fopts);
fclose(f);
if (res != 0)
die("compile_filter failed");
diff --git a/syscall_filter.c b/syscall_filter.c
index 4f8abe5..163cb0b 100644
--- a/syscall_filter.c
+++ b/syscall_filter.c
@@ -699,6 +699,7 @@ int compile_file(const char *filename, FILE *policy_file,
free_block_list(*arg_blocks);
*arg_blocks = NULL;
}
+ warn("could not allocate filter block");
ret = -1;
goto free_line;
}
@@ -717,6 +718,7 @@ int compile_file(const char *filename, FILE *policy_file,
free_block_list(*arg_blocks);
*arg_blocks = NULL;
}
+ warn("getmultiline() failed");
ret = -1;
}
diff --git a/syscall_filter_unittest.cc b/syscall_filter_unittest.cc
index 595c4cd..b82c2e3 100644
--- a/syscall_filter_unittest.cc
+++ b/syscall_filter_unittest.cc
@@ -38,22 +38,21 @@ enum ret_trap {
};
enum use_logging {
- NO_LOGGING = 0,
- USE_LOGGING = 1,
+ NO_LOGGING = 0,
+ USE_SIGSYS_LOGGING = 1,
+ USE_RET_LOG_LOGGING = 2,
};
int test_compile_filter(
std::string filename,
FILE* policy_file,
struct sock_fprog* prog,
- enum ret_trap use_ret_trap = USE_RET_KILL,
+ enum block_action action = ACTION_RET_KILL,
enum use_logging allow_logging = NO_LOGGING) {
- enum block_action action =
- (use_ret_trap == USE_RET_TRAP) ? ACTION_RET_TRAP : ACTION_RET_KILL;
struct filter_options filteropts {
.action = action,
- .allow_logging = allow_logging == USE_LOGGING,
- .allow_syscalls_for_logging = allow_logging == USE_LOGGING,
+ .allow_logging = allow_logging != NO_LOGGING,
+ .allow_syscalls_for_logging = allow_logging == USE_SIGSYS_LOGGING,
};
return compile_filter(filename.c_str(), policy_file, prog, &filteropts);
}
@@ -64,15 +63,13 @@ int test_compile_file(
struct filter_block* head,
struct filter_block** arg_blocks,
struct bpf_labels* labels,
- enum ret_trap use_ret_trap = USE_RET_KILL,
+ enum block_action action = ACTION_RET_KILL,
enum use_logging allow_logging = NO_LOGGING,
unsigned int include_level = 0) {
- enum block_action action =
- (use_ret_trap == USE_RET_TRAP) ? ACTION_RET_TRAP : ACTION_RET_KILL;
struct filter_options filteropts {
.action = action,
- .allow_logging = allow_logging == USE_LOGGING,
- .allow_syscalls_for_logging = allow_logging == USE_LOGGING,
+ .allow_logging = allow_logging != NO_LOGGING,
+ .allow_syscalls_for_logging = allow_logging == USE_SIGSYS_LOGGING,
};
return compile_file(filename.c_str(), policy_file, head, arg_blocks, labels,
&filteropts, include_level);
@@ -84,10 +81,9 @@ struct filter_block* test_compile_policy_line(
std::string policy_line,
unsigned int label_id,
struct bpf_labels* labels,
- enum ret_trap do_ret_trap = USE_RET_KILL) {
- return compile_policy_line(
- state, nr, policy_line.c_str(), label_id, labels,
- do_ret_trap == USE_RET_TRAP ? ACTION_RET_TRAP : ACTION_RET_KILL);
+ enum block_action action = ACTION_RET_KILL) {
+ return compile_policy_line(state, nr, policy_line.c_str(), label_id,
+ labels, action);
}
} // namespace
@@ -536,6 +532,86 @@ TEST_F(ArgFilterTest, arg0_equals) {
free_block_list(block);
}
+TEST_F(ArgFilterTest, arg0_equals_trap) {
+ std::string fragment = "arg0 == 0";
+
+ struct filter_block* block = test_compile_policy_line(
+ &state_, nr_, fragment, id_, &labels_, ACTION_RET_TRAP);
+
+ ASSERT_NE(block, nullptr);
+ size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2;
+ EXPECT_EQ(block->total_len, exp_total_len);
+
+ /* First block is a label. */
+ struct filter_block* curr_block = block;
+ ASSERT_NE(curr_block, nullptr);
+ EXPECT_EQ(curr_block->len, 1U);
+ EXPECT_LBL(curr_block->instrs);
+
+ /* Second block is a comparison. */
+ curr_block = curr_block->next;
+ EXPECT_COMP(curr_block);
+
+ /* Third block is a jump and a label (end of AND group). */
+ curr_block = curr_block->next;
+ ASSERT_NE(curr_block, nullptr);
+ EXPECT_GROUP_END(curr_block);
+
+ /* Fourth block is SECCOMP_RET_TRAP. */
+ curr_block = curr_block->next;
+ ASSERT_NE(curr_block, nullptr);
+ EXPECT_TRAP(curr_block);
+
+ /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
+ curr_block = curr_block->next;
+ ASSERT_NE(curr_block, nullptr);
+ EXPECT_ALLOW(curr_block);
+
+ EXPECT_EQ(curr_block->next, nullptr);
+
+ free_block_list(block);
+}
+
+TEST_F(ArgFilterTest, arg0_equals_log) {
+ std::string fragment = "arg0 == 0";
+
+ struct filter_block* block = test_compile_policy_line(
+ &state_, nr_, fragment, id_, &labels_, ACTION_RET_LOG);
+
+ ASSERT_NE(block, nullptr);
+ size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2;
+ EXPECT_EQ(block->total_len, exp_total_len);
+
+ /* First block is a label. */
+ struct filter_block* curr_block = block;
+ ASSERT_NE(curr_block, nullptr);
+ EXPECT_EQ(curr_block->len, 1U);
+ EXPECT_LBL(curr_block->instrs);
+
+ /* Second block is a comparison. */
+ curr_block = curr_block->next;
+ EXPECT_COMP(curr_block);
+
+ /* Third block is a jump and a label (end of AND group). */
+ curr_block = curr_block->next;
+ ASSERT_NE(curr_block, nullptr);
+ EXPECT_GROUP_END(curr_block);
+
+ /* Fourth block is SECCOMP_RET_LOG. */
+ curr_block = curr_block->next;
+ ASSERT_NE(curr_block, nullptr);
+ EXPECT_LOG(curr_block);
+
+ /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
+ curr_block = curr_block->next;
+ ASSERT_NE(curr_block, nullptr);
+ EXPECT_ALLOW(curr_block);
+
+ EXPECT_EQ(curr_block->next, nullptr);
+
+ free_block_list(block);
+}
+
TEST_F(ArgFilterTest, arg0_short_gt_ge_comparisons) {
for (std::string fragment :
{"arg1 < 0xff", "arg1 <= 0xff", "arg1 > 0xff", "arg1 >= 0xff"}) {
@@ -942,7 +1018,7 @@ TEST_F(ArgFilterTest, log_no_ret_error) {
struct filter_block* block =
test_compile_policy_line(&state_, nr_, fragment, id_, &labels_,
- USE_RET_TRAP);
+ ACTION_RET_TRAP);
ASSERT_NE(block, nullptr);
size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2;
@@ -1026,7 +1102,7 @@ TEST_F(ArgFilterTest, no_log_bad_ret_error) {
struct filter_block* block =
test_compile_policy_line(&state_, nr_, fragment, id_, &labels_,
- USE_RET_TRAP);
+ ACTION_RET_TRAP);
ASSERT_NE(block, nullptr);
size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2;
EXPECT_EQ(block->total_len, exp_total_len);
@@ -1358,7 +1434,8 @@ TEST(FilterTest, seccomp_mode1_trap) {
FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
- int res = test_compile_filter("policy", policy_file, &actual, USE_RET_TRAP);
+ int res =
+ test_compile_filter("policy", policy_file, &actual, ACTION_RET_TRAP);
fclose(policy_file);
/*
@@ -1385,6 +1462,66 @@ TEST(FilterTest, seccomp_mode1_trap) {
free(actual.filter);
}
+TEST(FilterTest, seccomp_mode1_log) {
+ struct sock_fprog actual;
+ std::string policy =
+ "read: 1\n"
+ "write: 1\n"
+ "rt_sigreturn: 1\n"
+ "exit: 1\n";
+
+ FILE* policy_file = write_policy_to_pipe(policy);
+ ASSERT_NE(policy_file, nullptr);
+
+ int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_LOG,
+ USE_RET_LOG_LOGGING);
+ fclose(policy_file);
+
+ /*
+ * Checks return value, filter length, and that the filter
+ * validates arch, loads syscall number, and
+ * only allows expected syscalls.
+ */
+ ASSERT_EQ(res, 0);
+ EXPECT_EQ(actual.len, 13);
+ EXPECT_ARCH_VALIDATION(actual.filter);
+ EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN,
+ BPF_LD+BPF_W+BPF_ABS, syscall_nr);
+ EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 1,
+ __NR_read);
+ EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 3,
+ __NR_write);
+ EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 5,
+ __NR_rt_sigreturn);
+ EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 7,
+ __NR_exit);
+ EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN + 9, BPF_RET+BPF_K,
+ SECCOMP_RET_LOG);
+
+ free(actual.filter);
+}
+
+TEST(FilterTest, seccomp_mode1_log_fails) {
+ struct sock_fprog actual;
+ std::string policy =
+ "read: 1\n"
+ "write: 1\n"
+ "rt_sigreturn: 1\n"
+ "exit: 1\n";
+
+ FILE* policy_file = write_policy_to_pipe(policy);
+ ASSERT_NE(policy_file, nullptr);
+
+ int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_LOG,
+ NO_LOGGING);
+ fclose(policy_file);
+
+ /*
+ * ACTION_RET_LOG should never be used without allowing logging.
+ */
+ ASSERT_EQ(res, -1);
+}
+
TEST(FilterTest, seccomp_read_write) {
struct sock_fprog actual;
std::string policy =
@@ -1525,8 +1662,8 @@ TEST(FilterTest, log) {
FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
- int res = test_compile_filter("policy", policy_file, &actual, USE_RET_TRAP,
- USE_LOGGING);
+ int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_TRAP,
+ USE_SIGSYS_LOGGING);
fclose(policy_file);
size_t i;
@@ -1572,8 +1709,8 @@ TEST(FilterTest, allow_log_but_kill) {
FILE* policy_file = write_policy_to_pipe(policy);
ASSERT_NE(policy_file, nullptr);
- int res = test_compile_filter("policy", policy_file, &actual, USE_RET_KILL,
- USE_LOGGING);
+ int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_KILL,
+ USE_SIGSYS_LOGGING);
fclose(policy_file);
size_t i;
@@ -1581,7 +1718,7 @@ TEST(FilterTest, allow_log_but_kill) {
/*
* Checks return value, filter length, and that the filter
* validates arch, loads syscall number, only allows expected syscalls,
- * and returns TRAP on failure.
+ * and kills on failure.
* NOTE(jorgelo): the filter is longer since we add the syscalls needed
* for logging.
*/
@@ -1726,7 +1863,7 @@ TEST(FilterTest, include) {
FILE* file_plain = write_policy_to_pipe(policy_plain);
ASSERT_NE(file_plain, nullptr);
int res_plain = test_compile_filter("policy", file_plain, &compiled_plain,
- USE_RET_KILL);
+ ACTION_RET_KILL);
fclose(file_plain);
std::string policy_with_include =
@@ -1734,9 +1871,8 @@ TEST(FilterTest, include) {
FILE* file_with_include = write_policy_to_pipe(policy_with_include);
ASSERT_NE(file_with_include, nullptr);
- int res_with_include =
- test_compile_filter("policy", file_with_include, &compiled_with_include,
- USE_RET_KILL);
+ int res_with_include = test_compile_filter(
+ "policy", file_with_include, &compiled_with_include, ACTION_RET_KILL);
fclose(file_with_include);
/*
diff --git a/syscall_filter_unittest_macros.h b/syscall_filter_unittest_macros.h
index 4923fc4..3972cb4 100644
--- a/syscall_filter_unittest_macros.h
+++ b/syscall_filter_unittest_macros.h
@@ -3,6 +3,11 @@
* found in the LICENSE file.
*/
+#ifndef SYSCALL_FILTER_UNITTEST_MACROS_H
+#define SYSCALL_FILTER_UNITTEST_MACROS_H
+
+#include "bpf.h"
+
/* BPF testing macros. */
#define EXPECT_EQ_BLOCK(_block, _code, _k, _jt, _jf) \
do { \
@@ -76,6 +81,13 @@ do { \
BPF_RET+BPF_K, SECCOMP_RET_TRAP); \
} while (0)
+#define EXPECT_LOG(_block) \
+do { \
+ EXPECT_EQ((_block)->len, 1U); \
+ EXPECT_EQ_STMT((_block)->instrs, \
+ BPF_RET+BPF_K, SECCOMP_RET_LOG); \
+} while (0)
+
#define EXPECT_ALLOW(_block) \
do { \
EXPECT_EQ((_block)->len, 2U); \
@@ -107,3 +119,5 @@ do { \
EXPECT_EQ_BLOCK(&(_filter)[1], \
BPF_JMP+BPF_JA, (_id), (_jt), (_jf)); \
} while (0)
+
+#endif // SYSCALL_FILTER_UNITTEST_MACROS_H
diff --git a/system.c b/system.c
index 2dcf152..175e088 100644
--- a/system.c
+++ b/system.c
@@ -464,3 +464,61 @@ int lookup_group(const char *group, gid_t *gid)
*gid = pgr->gr_gid;
return 0;
}
+
+static int seccomp_action_is_available(const char *wanted)
+{
+ if (is_android()) {
+ /*
+ * Accessing |actions_avail| is generating SELinux denials, so
+ * skip for now.
+ * TODO(crbug.com/978022, jorgelo): Remove once the denial is
+ * fixed.
+ */
+ return 0;
+ }
+ const char actions_avail_path[] =
+ "/proc/sys/kernel/seccomp/actions_avail";
+ FILE *f = fopen(actions_avail_path, "re");
+
+ if (!f) {
+ pwarn("fopen(%s) failed", actions_avail_path);
+ return 0;
+ }
+
+ char *actions_avail = NULL;
+ size_t buf_size = 0;
+ if (getline(&actions_avail, &buf_size, f) < 0) {
+ pwarn("getline() failed");
+ free(actions_avail);
+ return 0;
+ }
+
+ /*
+ * This is just substring search, which means that partial matches will
+ * match too (e.g. "action" would match "longaction"). There are no
+ * seccomp actions which include other actions though, so we're good for
+ * now. Eventually we might want to split the string by spaces.
+ */
+ return strstr(actions_avail, wanted) != NULL;
+}
+
+int seccomp_ret_log_available(void)
+{
+ static int ret_log_available = -1;
+
+ if (ret_log_available == -1)
+ ret_log_available = seccomp_action_is_available("log");
+
+ return ret_log_available;
+}
+
+int seccomp_ret_kill_process_available(void)
+{
+ static int ret_kill_process_available = -1;
+
+ if (ret_kill_process_available == -1)
+ ret_kill_process_available =
+ seccomp_action_is_available("kill_process");
+
+ return ret_kill_process_available;
+}
diff --git a/system.h b/system.h
index cd1a98a..7369846 100644
--- a/system.h
+++ b/system.h
@@ -60,6 +60,9 @@ int setup_mount_destination(const char *source, const char *dest, uid_t uid,
int lookup_user(const char *user, uid_t *uid, gid_t *gid);
int lookup_group(const char *group, gid_t *gid);
+int seccomp_ret_log_available(void);
+int seccomp_ret_kill_process_available(void);
+
#ifdef __cplusplus
}; /* extern "C" */
#endif
diff --git a/system_unittest.cc b/system_unittest.cc
index 36d8dc3..470421d 100644
--- a/system_unittest.cc
+++ b/system_unittest.cc
@@ -362,3 +362,8 @@ TEST(setup_mount_destination, create_char_dev) {
// We check it's a directory by deleting it as such.
EXPECT_EQ(0, rmdir(child_dev.c_str()));
}
+
+TEST(seccomp_actions_available, smoke) {
+ seccomp_ret_log_available();
+ seccomp_ret_kill_process_available();
+}
diff --git a/util.h b/util.h
index 02e7c23..a71fd29 100644
--- a/util.h
+++ b/util.h
@@ -134,6 +134,14 @@ static inline bool running_with_asan(void)
return compiled_with_asan() || &__asan_init != 0 || &__hwasan_init != 0;
}
+static inline bool debug_logging_allowed(void) {
+#if defined(ALLOW_DEBUG_LOGGING)
+ return true;
+#else
+ return false;
+#endif
+}
+
int lookup_syscall(const char *name);
const char *lookup_syscall_name(int nr);