blob: 3223a324baf19bcd8e3e29b851cb88be759c9c00 [file] [log] [blame]
/* syscall_filter_unittest.c
* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Test syscall filtering.
*/
#include <errno.h>
#include "test_harness.h"
#include "bpf.h"
#include "syscall_filter.h"
/* BPF testing macros. */
#define EXPECT_EQ_BLOCK(_block, _code, _k, _jt, _jf) \
do { \
EXPECT_EQ((_block)->code, _code); \
EXPECT_EQ((_block)->k, (unsigned int)(_k)); \
EXPECT_EQ((_block)->jt, _jt); \
EXPECT_EQ((_block)->jf, _jf); \
} while (0)
#define EXPECT_EQ_STMT(_block, _code, _k) \
EXPECT_EQ_BLOCK(_block, _code, _k, 0, 0)
#define EXPECT_COMP(_block) \
do { \
EXPECT_EQ((_block)->len, BPF_ARG_COMP_LEN + 1); \
EXPECT_EQ((_block)->instrs->code, BPF_LD+BPF_W+BPF_ABS); \
} while (0)
#define EXPECT_LBL(_block) \
do { \
EXPECT_EQ((_block)->code, BPF_JMP+BPF_JA); \
EXPECT_EQ((_block)->jt, LABEL_JT); \
EXPECT_EQ((_block)->jf, LABEL_JF); \
} while (0)
#define EXPECT_JUMP_LBL(_block) \
do { \
EXPECT_EQ((_block)->code, BPF_JMP+BPF_JA); \
EXPECT_EQ((_block)->jt, JUMP_JT); \
EXPECT_EQ((_block)->jf, JUMP_JF); \
} while (0)
#define EXPECT_GROUP_END(_block) \
do { \
EXPECT_EQ((_block)->len, 2U); \
EXPECT_JUMP_LBL(&(_block)->instrs[0]); \
EXPECT_LBL(&(_block)->instrs[1]); \
} while (0)
#define EXPECT_KILL(_block) \
do { \
EXPECT_EQ((_block)->len, 1U); \
EXPECT_EQ_STMT(_block->instrs, \
BPF_RET+BPF_K, SECCOMP_RET_KILL); \
} while (0)
#define EXPECT_ALLOW(_block) \
do { \
EXPECT_EQ((_block)->len, 2U); \
EXPECT_LBL(&(_block)->instrs[0]); \
EXPECT_EQ_STMT(&(_block)->instrs[1], \
BPF_RET+BPF_K, SECCOMP_RET_ALLOW); \
} while (0)
FIXTURE(bpf) {};
FIXTURE_SETUP(bpf) {}
FIXTURE_TEARDOWN(bpf) {}
TEST_F(bpf, set_bpf_instr) {
struct sock_filter instr;
unsigned char code = BPF_LD+BPF_W+BPF_ABS;
unsigned int k = 4;
unsigned char jt = 1, jf = 2;
size_t len = set_bpf_instr(&instr, code, k, jt, jf);
EXPECT_EQ(len, 1U);
EXPECT_EQ_BLOCK(&instr, code, k, jt, jf);
}
TEST_F(bpf, bpf_load_arg) {
struct sock_filter load_arg[BPF_LOAD_ARG_LEN];
int argidx = 1;
size_t len = bpf_load_arg(load_arg, argidx);
EXPECT_EQ(len, BPF_LOAD_ARG_LEN);
EXPECT_EQ_STMT(&load_arg[0], BPF_LD+BPF_W+BPF_ABS, LO_ARG(argidx));
EXPECT_EQ_STMT(&load_arg[1], BPF_ST, 0);
EXPECT_EQ_STMT(&load_arg[2], BPF_LD+BPF_W+BPF_ABS, HI_ARG(argidx));
EXPECT_EQ_STMT(&load_arg[3], BPF_ST, 1);
}
TEST_F(bpf, bpf_comp_jeq) {
struct sock_filter comp_jeq[BPF_COMP_LEN];
unsigned long c = 1;
unsigned char jt = 1;
unsigned char jf = 2;
size_t len = bpf_comp_jeq64(comp_jeq, c, jt, jf);
EXPECT_EQ(len, BPF_COMP_LEN);
EXPECT_EQ_BLOCK(&comp_jeq[0],
BPF_JMP+BPF_JEQ+BPF_K, 0, 0, jf + 2);
EXPECT_EQ_STMT(&comp_jeq[1], BPF_LD+BPF_MEM, 0);
EXPECT_EQ_BLOCK(&comp_jeq[2],
BPF_JMP+BPF_JEQ+BPF_K, c, jt, jf);
}
TEST_F(bpf, bpf_arg_comp) {
struct sock_filter *arg_comp;
int op = EQ;
int argidx = 1;
unsigned long c = 3;
unsigned int label_id = 0;
size_t len = bpf_arg_comp(&arg_comp, op, argidx, c, label_id);
EXPECT_EQ(len, BPF_ARG_COMP_LEN + 1);
EXPECT_EQ_STMT(&arg_comp[0],
BPF_LD+BPF_W+BPF_ABS, LO_ARG(argidx));
EXPECT_EQ_STMT(&arg_comp[1], BPF_ST, 0);
EXPECT_EQ_STMT(&arg_comp[2],
BPF_LD+BPF_W+BPF_ABS, HI_ARG(argidx));
EXPECT_EQ_STMT(&arg_comp[3], BPF_ST, 1);
EXPECT_EQ_BLOCK(&arg_comp[4],
BPF_JMP+BPF_JEQ+BPF_K, 0, 0, 2);
EXPECT_EQ_STMT(&arg_comp[5], BPF_LD+BPF_MEM, 0);
EXPECT_EQ_BLOCK(&arg_comp[6],
BPF_JMP+BPF_JEQ+BPF_K, c, 1, 0);
EXPECT_JUMP_LBL(&arg_comp[7]);
free(arg_comp);
}
FIXTURE(arg_filter) {
struct bpf_labels labels;
};
FIXTURE_SETUP(arg_filter) {}
FIXTURE_TEARDOWN(arg_filter) {}
TEST_F(arg_filter, arg0_equals) {
const char *fragment = "arg0 == 0";
int nr = 1;
unsigned int id = 0;
struct filter_block *block =
compile_section(nr, fragment, id, &self->labels);
ASSERT_NE(block, NULL);
EXPECT_EQ(block->total_len, 14U);
/* First block is a label. */
struct filter_block *curr_block = block;
ASSERT_NE(curr_block, NULL);
EXPECT_EQ(block->len, 1U);
EXPECT_LBL(curr_block->instrs);
/* Second block is a comparison. */
curr_block = block->next;
EXPECT_COMP(curr_block);
/* Third block is a jump and a label (end of AND group). */
curr_block = curr_block->next;
EXPECT_NE(curr_block, NULL);
EXPECT_GROUP_END(curr_block);
/* Fourth block is SECCOMP_RET_KILL */
curr_block = curr_block->next;
EXPECT_NE(curr_block, NULL);
EXPECT_KILL(curr_block);
/* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW */
curr_block = curr_block->next;
EXPECT_NE(curr_block, NULL);
EXPECT_ALLOW(curr_block);
EXPECT_EQ(curr_block->next, NULL);
free_block_list(block);
free_label_strings(&self->labels);
}
TEST_F(arg_filter, and_or) {
const char *fragment = "arg0 == 0 && arg1 == 0 || arg0 == 1";
int nr = 1;
unsigned int id = 0;
struct filter_block *block =
compile_section(nr, fragment, id, &self->labels);
ASSERT_NE(block, NULL);
EXPECT_EQ(block->total_len, 32U);
/* First block is a label. */
struct filter_block *curr_block = block;
ASSERT_NE(curr_block, NULL);
EXPECT_EQ(block->len, 1U);
EXPECT_LBL(curr_block->instrs);
/* Second block is a comparison ("arg0 == 0"). */
curr_block = curr_block->next;
EXPECT_NE(curr_block, NULL);
EXPECT_COMP(curr_block);
/* Third block is a comparison ("arg1 == 0"). */
curr_block = curr_block->next;
EXPECT_NE(curr_block, NULL);
EXPECT_COMP(curr_block);
/* Fourth block is a jump and a label (end of AND group). */
curr_block = curr_block->next;
EXPECT_NE(curr_block, NULL);
EXPECT_GROUP_END(curr_block);
/* Fifth block is a comparison ("arg0 == 1"). */
curr_block = curr_block->next;
EXPECT_NE(curr_block, NULL);
EXPECT_COMP(curr_block);
/* Sixth block is a jump and a label (end of AND group). */
curr_block = curr_block->next;
EXPECT_NE(curr_block, NULL);
EXPECT_GROUP_END(curr_block);
/* Seventh block is SECCOMP_RET_KILL */
curr_block = curr_block->next;
EXPECT_NE(curr_block, NULL);
EXPECT_KILL(curr_block);
/* Eigth block is "SUCCESS" label and SECCOMP_RET_ALLOW */
curr_block = curr_block->next;
EXPECT_NE(curr_block, NULL);
EXPECT_ALLOW(curr_block);
EXPECT_EQ(curr_block->next, NULL);
free_block_list(block);
free_label_strings(&self->labels);
}
TEST_F(arg_filter, ret_errno) {
const char *fragment = "arg0 == 0 || arg0 == 1; return 1";
int nr = 1;
unsigned int id = 0;
struct filter_block *block =
compile_section(nr, fragment, id, &self->labels);
ASSERT_NE(block, NULL);
EXPECT_EQ(block->total_len, 24U);
/* First block is a label. */
struct filter_block *curr_block = block;
ASSERT_NE(curr_block, NULL);
EXPECT_EQ(block->len, 1U);
EXPECT_LBL(curr_block->instrs);
/* Second block is a comparison ("arg0 == 0"). */
curr_block = curr_block->next;
EXPECT_NE(curr_block, NULL);
EXPECT_COMP(curr_block);
/* Third block is a jump and a label (end of AND group). */
curr_block = curr_block->next;
EXPECT_NE(curr_block, NULL);
EXPECT_GROUP_END(curr_block);
/* Fourth block is a comparison ("arg0 == 1"). */
curr_block = curr_block->next;
EXPECT_NE(curr_block, NULL);
EXPECT_COMP(curr_block);
/* Fifth block is a jump and a label (end of AND group). */
curr_block = curr_block->next;
EXPECT_NE(curr_block, NULL);
EXPECT_GROUP_END(curr_block);
/* Sixth block is SECCOMP_RET_ERRNO */
curr_block = curr_block->next;
EXPECT_NE(curr_block, NULL);
EXPECT_EQ(curr_block->len, 1U);
EXPECT_EQ_STMT(curr_block->instrs,
BPF_RET+BPF_K,
SECCOMP_RET_ERRNO | (1 & SECCOMP_RET_DATA));
/* Seventh block is "SUCCESS" label and SECCOMP_RET_ALLOW */
curr_block = curr_block->next;
EXPECT_NE(curr_block, NULL);
EXPECT_ALLOW(curr_block);
EXPECT_EQ(curr_block->next, NULL);
free_block_list(block);
free_label_strings(&self->labels);
}
TEST_F(arg_filter, invalid) {
const char *fragment = "argnn == 0";
int nr = 1;
unsigned int id = 0;
struct filter_block *block =
compile_section(nr, fragment, id, &self->labels);
ASSERT_EQ(block, NULL);
fragment = "arg0 == 0 && arg1 == 1; return errno";
block = compile_section(nr, fragment, id, &self->labels);
ASSERT_EQ(block, NULL);
}
TEST_HARNESS_MAIN