aboutgitcodelistschat:MatrixIRC
diff options
context:
space:
mode:
-rw-r--r--cooker/filter.c75
-rw-r--r--cooker/filter.h36
-rw-r--r--tests/unit/test_filter.c76
-rw-r--r--tests/unit/test_filter_build.c21
-rw-r--r--tests/unit/testutil.h7
-rw-r--r--tests/unit/util.c14
6 files changed, 195 insertions, 34 deletions
diff --git a/cooker/filter.c b/cooker/filter.c
index 350d6bc..29c2d64 100644
--- a/cooker/filter.c
+++ b/cooker/filter.c
@@ -9,6 +9,7 @@
#include "numbers.h"
#include "filter.h"
+#include "util.h"
#define N_SYSCALL sizeof(numbers) / sizeof(numbers[0])
@@ -157,7 +158,7 @@ static unsigned get_n_args_syscall_entry(const struct bpf_call *entry)
unsigned i, n = 0;
for (i = 0; i < 6; i++)
- if (entry->check_arg[i])
+ if (entry->args[i].type != NO_CHECK)
n++;
return n;
}
@@ -181,22 +182,35 @@ static unsigned int get_n_args_syscall_instr(const struct syscall_entry *table)
for (unsigned int i = 0; i < table->count; i++) {
entry = table->entry + i;
n = 0;
-
- /* For every argument there are 2 instructions, one to
- * load the value and the second to evaluate the
- * argument
- */
for (unsigned int k = 0; k < 6; k++) {
- if (entry->check_arg[k]) {
+ switch (entry->args[k].type) {
+ case U32:
+ /* For 32 bit arguments: 2 instructions,
+ * 1 for loading the value and
+ * 1 for evaluating the argument */
n += 2;
+ break;
+ case U64:
+ /* For 64 bit arguments: 4 instructions, for
+ * loading and evaluating the high and low 32
+ * bits chuncks.
+ */
+ n += 4;
+ break;
+ case NO_CHECK:
+ break;
}
}
total_instr += n;
+ /* If there at least an argument, then there is the jump to the
+ * notification */
if (n > 0) {
has_arg = true;
total_instr++;
}
}
+ /* If there at least an argument for that syscall, then there is the jump to the
+ * accept */
if (has_arg)
total_instr++;
@@ -215,9 +229,12 @@ static unsigned int get_total_args_instr(const struct syscall_entry table[],
}
static bool check_args_syscall_entry(const struct bpf_call *entry){
- return entry->check_arg[0] || entry->check_arg[1] ||
- entry->check_arg[2] || entry->check_arg[3] ||
- entry->check_arg[4] || entry->check_arg[5];
+ return entry->args[0].type != NO_CHECK ||
+ entry->args[1].type != NO_CHECK ||
+ entry->args[2].type != NO_CHECK ||
+ entry->args[3].type != NO_CHECK ||
+ entry->args[4].type != NO_CHECK ||
+ entry->args[5].type != NO_CHECK;
}
static bool check_args_syscall(const struct syscall_entry *table)
@@ -243,6 +260,21 @@ unsigned int create_bpf_program_log(struct sock_filter filter[])
return 4;
}
+static unsigned int eq_u64_filter(struct sock_filter filter[], int idx,
+ uint64_t v, unsigned int jtrue,
+ unsigned int jfalse)
+{
+ uint32_t hi = get_hi(v);
+ uint32_t lo = get_lo(v);
+
+ filter[0] = (struct sock_filter)LOAD(LO_ARG(idx));
+ filter[1] = (struct sock_filter)EQ(lo, 0, jfalse);
+ filter[2] = (struct sock_filter)LOAD(HI_ARG(idx));
+ filter[3] = (struct sock_filter)EQ(hi, jtrue, jfalse);
+
+ return 4;
+}
+
unsigned int create_bfp_program(struct syscall_entry table[],
struct sock_filter filter[],
unsigned int n_syscall)
@@ -320,16 +352,33 @@ unsigned int create_bfp_program(struct syscall_entry table[],
entry = table[i].entry + j;
next_args_off = get_n_args_syscall_entry(entry);
for (k = 0; k < 6; k++) {
- if (entry->check_arg[k]) {
+ offset = next_args_off - n_checks;
+ switch (entry->args[k].type) {
+ case NO_CHECK:
+ break;
+ case U64:
+ size += eq_u64_filter(
+ &filter[size], k,
+ entry->args[k].value.v64, 0,
+ offset);
+ n_checks++;
+ has_arg = true;
+ break;
+ case U32:
filter[size++] = (struct sock_filter)
LOAD((offsetof(
struct seccomp_data,
args[k])));
filter[size++] = (struct sock_filter)EQ(
- (table[i].entry + j)->args[k],
- 0, next_args_off - n_checks);
+ entry->args[k].value.v32, 0,
+ offset);
n_checks++;
has_arg = true;
+ break;
+ default:
+ fprintf(stderr,
+ "value for args not recognized\n");
+ return -1;
}
}
if (check_args_syscall_entry(table[i].entry))
diff --git a/cooker/filter.h b/cooker/filter.h
index c8e74be..7705414 100644
--- a/cooker/filter.h
+++ b/cooker/filter.h
@@ -1,25 +1,53 @@
#ifndef FILTER_H_
#define FILTER_H_
+#include <stdint.h>
+
#include <linux/filter.h>
#include <linux/audit.h>
#include <linux/seccomp.h>
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define ENDIAN(_lo, _hi) _lo, _hi
+#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)])
+#define HI_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32)
+#define get_hi(x) ((uint32_t)((x) >> 32))
+#define get_lo(x) ((uint32_t)((x)&0xffffffff))
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define ENDIAN(_lo, _hi) _hi, _lo
+#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32)
+#define HI_ARG(idx) offsetof(struct seccomp_data, args[(idx)])
+#define get_lo(x) ((uint32_t)((x) >> 32))
+#define get_hi(x) ((uint32_t)((x)&0xffffffff))
+#else
+#error "Unknown endianness"
+#endif
+
#define JGE(nr, right, left) \
BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, (nr), (right), (left))
#define JUMPA(jump) BPF_JUMP(BPF_JMP | BPF_JA, (jump), 0, 0)
-#define EQ(nr, a1, a2) BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (nr), (a1), (a2))
+#define EQ(x, a1, a2) BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (x), (a1), (a2))
#define LOAD(x) BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (x))
-
#define MAX_FILTER 1024
#define MAX_JUMPS 128
#define EMPTY -1
+enum arg_type { NO_CHECK, U32, U64 };
+
+union arg_value {
+ uint32_t v32;
+ uint64_t v64;
+};
+
+struct arg {
+ enum arg_type type;
+ union arg_value value;
+};
+
struct bpf_call {
char *name;
- int args[6];
- bool check_arg[6];
+ struct arg args[6];
};
struct syscall_entry {
diff --git a/tests/unit/test_filter.c b/tests/unit/test_filter.c
index c1e0949..4d7c9d8 100644
--- a/tests/unit/test_filter.c
+++ b/tests/unit/test_filter.c
@@ -6,6 +6,8 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
+#include <stddef.h>
+#include <fcntl.h>
#include <sys/syscall.h>
#include <sys/mman.h>
#include <sys/resource.h>
@@ -29,14 +31,26 @@ static int generate_install_filter(struct args_target *at)
unsigned int size;
for (i = 0; i < 6; i++) {
- if (at->args[i] != NULL) {
- calls[0].args[i] = (int)at->args[i];
- calls[0].check_arg[i] = true;
- } else {
- calls[0].check_arg[i] = false;
+ if (at->args[i] == NULL) {
+ calls[0].args[i].type = NO_CHECK;
+ continue;
+ }
+ switch (at->arg_type[i]) {
+ case U32:
+ calls[0].args[i].value.v32 = (uint32_t)at->args[i];
+ calls[0].args[i].type = U32;
+ break;
+ case U64:
+ calls[0].args[i].value.v64 = (uint64_t)at->args[i];
+ calls[0].args[i].type = U64;
+ break;
+ case NO_CHECK:
+ calls[0].args[i].type = NO_CHECK;
+ break;
}
}
size = create_bfp_program(table, filter, 1);
+ bpf_disasm_all(filter, size);
return install_filter(filter, size);
}
@@ -46,6 +60,7 @@ START_TEST(no_args)
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
at->check_fd = false;
at->nr = __NR_getpid;
+ set_args_no_check(at);
at->install_filter = generate_install_filter;
setup();
mock_syscall_target();
@@ -59,7 +74,9 @@ START_TEST(with_getsid)
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
at->check_fd = false;
at->nr = __NR_getsid;
+ set_args_no_check(at);
at->args[0] = &id;
+ at->arg_type[0] = U32;
at->install_filter = generate_install_filter;
setup();
mock_syscall_target();
@@ -74,19 +91,59 @@ START_TEST(with_getpriority)
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
at->check_fd = false;
at->nr = __NR_getpriority;
+ set_args_no_check(at);
at->args[0] = &which;
+ at->arg_type[0] = U32;
at->args[1] = &who;
+ at->arg_type[0] = U32;
+ at->install_filter = generate_install_filter;
+ setup();
+ mock_syscall_target();
+}
+END_TEST
+
+static int target_lseek()
+{
+ int fd = open("/dev/zero", O_RDWR);
+
+ /* Open the device on the target, but the arg0 isn't in the filter */
+ ck_assert_int_ge(fd, 0);
+ at->args[0] = fd;
+ return target();
+}
+
+static void test_lseek(off_t offset)
+{
+ at = mmap(NULL, sizeof(struct args_target), PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ at->check_fd = false;
+ at->nr = __NR_lseek;
+ at->target = target_lseek;
+ set_args_no_check(at);
+ at->args[1] = offset;
+ at->arg_type[1] = U64;
at->install_filter = generate_install_filter;
setup();
mock_syscall_target();
}
+
+START_TEST(with_lseek_lo)
+{
+ test_lseek(0x1);
+}
+END_TEST
+
+START_TEST(with_lseek_hi)
+{
+ test_lseek(0x0000000100000000);
+}
END_TEST
Suite *op_call_suite(void)
{
Suite *s;
int timeout = 30;
- TCase *simple, *args32;
+ TCase *simple, *args32, *args64;
s = suite_create("Test filter with target");
@@ -103,6 +160,13 @@ Suite *op_call_suite(void)
tcase_add_test(args32, with_getpriority);
suite_add_tcase(s, args32);
+ args64 = tcase_create("with args 64 bit");
+ tcase_add_checked_fixture(args64, NULL, teardown);
+ tcase_set_timeout(args32, timeout);
+ tcase_add_test(args64, with_lseek_lo);
+ tcase_add_test(args64, with_lseek_hi);
+ suite_add_tcase(s, args64);
+
return s;
}
diff --git a/tests/unit/test_filter_build.c b/tests/unit/test_filter_build.c
index 55e2a2b..4727e51 100644
--- a/tests/unit/test_filter_build.c
+++ b/tests/unit/test_filter_build.c
@@ -71,7 +71,8 @@ START_TEST(test_single_instr_two_args)
{
.name = "test1",
.args = { 0, 123, 321, 0, 0, 0 },
- .check_arg = { false, true, true, false, false, false },
+ .check_arg = { NO_CHECK, U32, U32, NO_CHECK, NO_CHECK,
+ NO_CHECK },
},
};
struct syscall_entry table[] = {
@@ -198,12 +199,14 @@ START_TEST(test_multiple_instr_with_args)
struct bpf_call calls[] = {
{ .name = "test1",
.args = { 0, 123, 321, 0, 0, 0 },
- .check_arg = { false, true, true, false, false, false } },
+ .check_arg = { NO_CHECK, U32, U32, NO_CHECK, NO_CHECK,
+ NO_CHECK } },
{ .name = "test2" },
{ .name = "test3" },
{ .name = "test4",
.args = { 0, 123, 321, 0, 0, 0 },
- .check_arg = { false, true, true, false, false, false } },
+ .check_arg = { NO_CHECK, U32, U32, NO_CHECK, NO_CHECK,
+ NO_CHECK } },
{ .name = "test5" },
};
struct syscall_entry table[] = {
@@ -272,18 +275,22 @@ START_TEST(test_multiple_instance_same_instr)
struct bpf_call calls[] = {
{ .name = "test1",
.args = { 0, 123, 0, 0, 0, 0 },
- .check_arg = { false, true, false, false, false, false } },
+ .check_arg = { NO_CHECK, U32, NO_CHECK, NO_CHECK, NO_CHECK,
+ NO_CHECK } },
{ .name = "test1",
.args = { 0, 0, 321, 0, 0, 0 },
- .check_arg = { false, false, true, false, false, false } },
+ .check_arg = { NO_CHECK, NO_CHECK, U32, NO_CHECK, NO_CHECK,
+ NO_CHECK } },
{ .name = "test2" },
{ .name = "test3" },
{ .name = "test4",
.args = { 0, 123, 0, 0, 0, 0 },
- .check_arg = { false, true, false, false, false, false } },
+ .check_arg = { NO_CHECK, U32, NO_CHECK, NO_CHECK, NO_CHECK,
+ NO_CHECK } },
{ .name = "test4",
.args = { 0, 0, 321, 0, 0, 0 },
- .check_arg = { false, false, true, false, false, false } },
+ .check_arg = { NO_CHECK, NO_CHECK, U32, NO_CHECK, NO_CHECK,
+ NO_CHECK } },
{ .name = "test5" },
};
struct syscall_entry table[] = {
diff --git a/tests/unit/testutil.h b/tests/unit/testutil.h
index dd4f1e9..7bb971a 100644
--- a/tests/unit/testutil.h
+++ b/tests/unit/testutil.h
@@ -5,8 +5,10 @@
#include <stdlib.h>
#include <stdbool.h>
#include <stddef.h>
+#include <limits.h>
#include <check.h>
+#include "filter.h"
#define STACK_SIZE (1024 * 1024 / 8)
@@ -17,8 +19,10 @@ struct args_target {
bool open_path;
int fd;
int nr;
+ enum arg_type arg_type[6];
void *args[6];
int (*install_filter)(struct args_target *at);
+ int (*target)(void *);
};
extern struct seccomp_notif req;
@@ -26,7 +30,7 @@ extern int notifyfd;
extern struct args_target *at;
extern int pipefd[2];
extern pid_t pid;
-extern char path[100];
+extern char path[PATH_MAX];
extern uint16_t tmp_data[TMP_DATA_SIZE];
@@ -43,5 +47,6 @@ void teardown();
int install_notification_filter(struct args_target *at);
void continue_target();
void mock_syscall_target();
+void set_args_no_check(struct args_target *at);
#endif /* TESTUTIL_H */
diff --git a/tests/unit/util.c b/tests/unit/util.c
index 5a1c5aa..7e5ec83 100644
--- a/tests/unit/util.c
+++ b/tests/unit/util.c
@@ -29,7 +29,7 @@ int notifyfd;
struct args_target *at;
int pipefd[2];
pid_t pid;
-char path[] = "/tmp/test-seitan";
+char path[PATH_MAX] = "/tmp/test-seitan";
uint16_t tmp_data[TMP_DATA_SIZE];
int install_notification_filter(struct args_target *at)
@@ -58,7 +58,7 @@ int target()
at->ret = syscall(at->nr, at->args[0], at->args[1], at->args[2],
at->args[3], at->args[4], at->args[5]);
- at->err = errno;
+ at->err = errno;
if (at->open_path) {
if ((at->fd = open(path, O_CREAT | O_RDONLY)) < 0) {
perror("open");
@@ -185,13 +185,21 @@ void mock_syscall_target()
ck_assert_msg(ret == 0, strerror(errno));
}
+void set_args_no_check(struct args_target *at)
+{
+ for (unsigned int i = 0; i < 6; i++)
+ at->arg_type[i] = NO_CHECK;
+}
+
void setup()
{
int ret;
signal(SIGCHLD, target_exit);
ck_assert_int_ne(pipe(pipefd), -1);
- pid = do_clone(target, NULL);
+ if (at->target == NULL)
+ at->target = target;
+ pid = do_clone(at->target, NULL);
ck_assert_int_ge(pid, 0);
/* Use write pipe to sync the target for checking the existance of the fd */