aboutsummaryrefslogtreecommitdiff
path: root/syscall_filter.c
diff options
context:
space:
mode:
Diffstat (limited to 'syscall_filter.c')
-rw-r--r--syscall_filter.c99
1 files changed, 96 insertions, 3 deletions
diff --git a/syscall_filter.c b/syscall_filter.c
index 2c389ae..fcdbaa8 100644
--- a/syscall_filter.c
+++ b/syscall_filter.c
@@ -123,6 +123,13 @@ void append_ret_kill(struct filter_block *head)
append_filter_block(head, filter, ONE_INSTR);
}
+void append_ret_kill_process(struct filter_block *head)
+{
+ struct sock_filter *filter = new_instr_buf(ONE_INSTR);
+ set_bpf_ret_kill_process(filter);
+ append_filter_block(head, filter, ONE_INSTR);
+}
+
void append_ret_trap(struct filter_block *head)
{
struct sock_filter *filter = new_instr_buf(ONE_INSTR);
@@ -154,12 +161,47 @@ void append_allow_syscall(struct filter_block *head, int nr)
append_filter_block(head, filter, len);
}
+void copy_parser_state(struct parser_state *src, struct parser_state *dest)
+{
+ const char *filename = strdup(src->filename);
+ if (!filename)
+ pdie("strdup(src->filename) failed");
+
+ dest->line_number = src->line_number;
+ dest->filename = filename;
+}
+
+/*
+ * Inserts the current state into the array of previous syscall states at the
+ * index |ind| if it is a newly encountered syscall. Returns true if it is a
+ * newly encountered syscall and false if it is a duplicate.
+ */
+bool insert_and_check_duplicate_syscall(struct parser_state **previous_syscalls,
+ struct parser_state *state, size_t ind)
+{
+ if (ind >= get_num_syscalls()) {
+ die("syscall index %zu out of range: %zu total syscalls", ind,
+ get_num_syscalls());
+ }
+ struct parser_state *prev_state_ptr = previous_syscalls[ind];
+ if (prev_state_ptr == NULL) {
+ previous_syscalls[ind] = calloc(1, sizeof(struct parser_state));
+ if (!previous_syscalls[ind])
+ die("could not allocate parser_state buffer");
+ copy_parser_state(state, previous_syscalls[ind]);
+ return true;
+ }
+ return false;
+}
+
void allow_logging_syscalls(struct filter_block *head)
{
unsigned int i;
+
for (i = 0; i < log_syscalls_len; i++) {
warn("allowing syscall: %s", log_syscalls[i]);
- append_allow_syscall(head, lookup_syscall(log_syscalls[i]));
+ append_allow_syscall(head,
+ lookup_syscall(log_syscalls[i], NULL));
}
}
@@ -303,6 +345,9 @@ int compile_errno(struct parser_state *state, struct filter_block *head,
case ACTION_RET_KILL:
append_ret_kill(head);
break;
+ case ACTION_RET_KILL_PROCESS:
+ append_ret_kill_process(head);
+ break;
case ACTION_RET_TRAP:
append_ret_trap(head);
break;
@@ -447,6 +492,9 @@ struct filter_block *compile_policy_line(struct parser_state *state, int nr,
case ACTION_RET_KILL:
append_ret_kill(head);
break;
+ case ACTION_RET_KILL_PROCESS:
+ append_ret_kill_process(head);
+ break;
case ACTION_RET_TRAP:
append_ret_trap(head);
break;
@@ -552,6 +600,10 @@ static ssize_t getmultiline(char **lineptr, size_t *n, FILE *stream)
/* Merge the lines. */
*n = ret + next_ret + 2;
line = realloc(line, *n);
+ if (!line) {
+ free(next_line);
+ return -1;
+ }
line[ret] = ' ';
memcpy(&line[ret + 1], next_line, next_ret + 1);
free(next_line);
@@ -563,6 +615,7 @@ int compile_file(const char *filename, FILE *policy_file,
struct filter_block *head, struct filter_block **arg_blocks,
struct bpf_labels *labels,
const struct filter_options *filteropts,
+ struct parser_state **previous_syscalls,
unsigned int include_level)
{
/* clang-format off */
@@ -626,6 +679,7 @@ int compile_file(const char *filename, FILE *policy_file,
}
if (compile_file(filename, included_file, head,
arg_blocks, labels, filteropts,
+ previous_syscalls,
include_level + 1) == -1) {
compiler_warn(&state, "'@include %s' failed",
filename);
@@ -657,7 +711,8 @@ int compile_file(const char *filename, FILE *policy_file,
}
syscall_name = strip(syscall_name);
- int nr = lookup_syscall(syscall_name);
+ size_t ind = 0;
+ int nr = lookup_syscall(syscall_name, &ind);
if (nr < 0) {
compiler_warn(&state, "nonexistent syscall '%s'",
syscall_name);
@@ -680,6 +735,16 @@ int compile_file(const char *filename, FILE *policy_file,
goto free_line;
}
+ if (!insert_and_check_duplicate_syscall(previous_syscalls,
+ &state, ind)) {
+ if (!filteropts->allow_duplicate_syscalls)
+ ret = -1;
+ compiler_warn(&state, "syscall %s redefined here",
+ lookup_syscall_name(nr));
+ compiler_warn(previous_syscalls[ind],
+ "previous definition here");
+ }
+
/*
* For each syscall, add either a simple ALLOW,
* or an arg filter block.
@@ -752,6 +817,14 @@ int compile_filter(const char *filename, FILE *initial_file,
struct filter_block *head = new_filter_block();
struct filter_block *arg_blocks = NULL;
+ /*
+ * Create the data structure that will keep track of what system
+ * calls we have already defined if the option is true.
+ */
+ size_t num_syscalls = get_num_syscalls();
+ struct parser_state **previous_syscalls =
+ calloc(num_syscalls, sizeof(*previous_syscalls));
+
/* Start filter by validating arch. */
struct sock_filter *valid_arch = new_instr_buf(ARCH_VALIDATION_LEN);
size_t len = bpf_validate_arch(valid_arch);
@@ -771,7 +844,8 @@ int compile_filter(const char *filename, FILE *initial_file,
allow_logging_syscalls(head);
if (compile_file(filename, initial_file, head, &arg_blocks, &labels,
- filteropts, 0 /* include_level */) != 0) {
+ filteropts, previous_syscalls,
+ 0 /* include_level */) != 0) {
warn("compile_filter: compile_file() failed");
ret = -1;
goto free_filter;
@@ -785,6 +859,9 @@ int compile_filter(const char *filename, FILE *initial_file,
case ACTION_RET_KILL:
append_ret_kill(head);
break;
+ case ACTION_RET_KILL_PROCESS:
+ append_ret_kill_process(head);
+ break;
case ACTION_RET_TRAP:
append_ret_trap(head);
break;
@@ -815,6 +892,8 @@ int compile_filter(const char *filename, FILE *initial_file,
struct sock_filter *final_filter =
calloc(final_filter_len, sizeof(struct sock_filter));
+ if (!final_filter)
+ die("could not allocate final BPF filter");
if (flatten_block_list(head, final_filter, 0, final_filter_len) < 0) {
free(final_filter);
@@ -842,6 +921,7 @@ free_filter:
free_block_list(head);
free_block_list(arg_blocks);
free_label_strings(&labels);
+ free_previous_syscalls(previous_syscalls);
return ret;
}
@@ -875,3 +955,16 @@ void free_block_list(struct filter_block *head)
free(prev);
}
}
+
+void free_previous_syscalls(struct parser_state **previous_syscalls)
+{
+ size_t num_syscalls = get_num_syscalls();
+ for (size_t i = 0; i < num_syscalls; i++) {
+ struct parser_state *state = previous_syscalls[i];
+ if (state) {
+ free((char *)state->filename);
+ free(state);
+ }
+ }
+ free(previous_syscalls);
+}