blob: 7c59c15c9e19b8c2a474d8d64f9dbdd90b778acf [file] [log] [blame]
#include "sandboxed_api/sandbox2/bpf_evaluator.h"
#include <linux/filter.h>
#include <cstdint>
#include <tuple>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/status/status.h"
#include "sandboxed_api/sandbox2/util/bpf_helper.h"
#include "sandboxed_api/util/status_matchers.h"
namespace sandbox2::bpf {
namespace {
using ::testing::Eq;
TEST(EvaluatorTest, SimpleReturn) {
sock_filter prog[] = {
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
};
SAPI_ASSERT_OK_AND_ASSIGN(uint32_t result, Evaluate(prog, {.nr = 1}));
EXPECT_THAT(result, Eq(SECCOMP_RET_ALLOW));
}
TEST(EvaluatorTest, ReturnAcumulator) {
sock_filter prog[] = {
BPF_STMT(BPF_LD + BPF_IMM, SECCOMP_RET_ALLOW),
BPF_STMT(BPF_RET + BPF_A, 0),
};
SAPI_ASSERT_OK_AND_ASSIGN(uint32_t result, Evaluate(prog, {.nr = 1}));
EXPECT_THAT(result, Eq(SECCOMP_RET_ALLOW));
}
TEST(EvaluatorTest, SimpleJump) {
sock_filter prog[] = {
LOAD_SYSCALL_NR,
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 1, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL),
};
SAPI_ASSERT_OK_AND_ASSIGN(uint32_t result, Evaluate(prog, {.nr = 1}));
EXPECT_THAT(result, Eq(SECCOMP_RET_ALLOW));
SAPI_ASSERT_OK_AND_ASSIGN(result, Evaluate(prog, {.nr = 2}));
EXPECT_THAT(result, Eq(SECCOMP_RET_KILL));
}
TEST(EvaluatorTest, AbsoluteJump) {
sock_filter prog[] = {
BPF_STMT(BPF_JMP + BPF_JA, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL),
};
SAPI_ASSERT_OK_AND_ASSIGN(uint32_t result, Evaluate(prog, {.nr = 1}));
EXPECT_THAT(result, Eq(SECCOMP_RET_KILL));
}
TEST(EvaluatorTest, MemoryOps) {
sock_filter prog[] = {
BPF_STMT(BPF_LD + BPF_IMM, 0),
BPF_STMT(BPF_LDX + BPF_IMM, 1),
BPF_STMT(BPF_STX, 5),
BPF_STMT(BPF_LD + BPF_MEM, 5),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 1, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL),
};
SAPI_ASSERT_OK_AND_ASSIGN(uint32_t result, Evaluate(prog, {}));
EXPECT_THAT(result, Eq(SECCOMP_RET_ALLOW));
}
TEST(EvaluatorTest, MemoryOps2) {
sock_filter prog[] = {
BPF_STMT(BPF_LDX + BPF_IMM, 1),
BPF_STMT(BPF_LD + BPF_IMM, 0),
BPF_STMT(BPF_ST, 5),
BPF_STMT(BPF_LDX + BPF_MEM, 5),
BPF_STMT(BPF_LD + BPF_IMM, 1),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL),
};
SAPI_ASSERT_OK_AND_ASSIGN(uint32_t result, Evaluate(prog, {}));
EXPECT_THAT(result, Eq(SECCOMP_RET_KILL));
}
TEST(EvaluatorTest, Txa) {
sock_filter prog[] = {
BPF_STMT(BPF_LDX + BPF_IMM, 1),
BPF_STMT(BPF_LD + BPF_IMM, 0),
BPF_STMT(BPF_MISC + BPF_TXA, 0),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 1, 0, 2),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL),
};
SAPI_ASSERT_OK_AND_ASSIGN(uint32_t result, Evaluate(prog, {}));
EXPECT_THAT(result, Eq(SECCOMP_RET_ALLOW));
}
TEST(EvaluatorTest, Tax) {
sock_filter prog[] = {
BPF_STMT(BPF_LDX + BPF_IMM, 1),
BPF_STMT(BPF_LD + BPF_IMM, 0),
BPF_STMT(BPF_MISC + BPF_TAX, 0),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 2),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL),
};
SAPI_ASSERT_OK_AND_ASSIGN(uint32_t result, Evaluate(prog, {}));
EXPECT_THAT(result, Eq(SECCOMP_RET_ALLOW));
}
TEST(EvaluatorTest, LoadLen) {
sock_filter prog[] = {
BPF_STMT(BPF_LD + BPF_LEN, 0),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct seccomp_data), 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL),
};
SAPI_ASSERT_OK_AND_ASSIGN(uint32_t result, Evaluate(prog, {}));
EXPECT_THAT(result, Eq(SECCOMP_RET_ALLOW));
}
TEST(EvaluatorTest, LoadLenX) {
sock_filter prog[] = {
BPF_STMT(BPF_LDX + BPF_LEN, 0),
BPF_STMT(BPF_LD + BPF_IMM, sizeof(struct seccomp_data)),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL),
};
SAPI_ASSERT_OK_AND_ASSIGN(uint32_t result, Evaluate(prog, {}));
EXPECT_THAT(result, Eq(SECCOMP_RET_ALLOW));
}
TEST(EvaluatorTest, AllJumps) {
std::vector<std::tuple<sock_filter, uint32_t, uint32_t>> jumps = {
{BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 1, 0, 1), 1, 2},
{BPF_JUMP(BPF_JMP + BPF_JGT + BPF_K, 1, 0, 1), 2, 1},
{BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, 1, 0, 1), 1, 0},
{BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 3, 0, 1), 2, 12},
};
for (const auto& [jmp, allow_nr, kill_nr] : jumps) {
std::vector<sock_filter> prog = {
LOAD_SYSCALL_NR,
};
prog.push_back(jmp);
prog.push_back(BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW));
prog.push_back(BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL));
SAPI_ASSERT_OK_AND_ASSIGN(
uint32_t result, Evaluate(prog, {.nr = static_cast<int>(allow_nr)}));
EXPECT_THAT(result, Eq(SECCOMP_RET_ALLOW));
SAPI_ASSERT_OK_AND_ASSIGN(
result, Evaluate(prog, {.nr = static_cast<int>(kill_nr)}));
EXPECT_THAT(result, Eq(SECCOMP_RET_KILL));
}
}
TEST(EvaluatorTest, Arithmetics) {
sock_filter prog[] = {
LOAD_SYSCALL_NR,
BPF_STMT(BPF_ALU + BPF_NEG, 1),
BPF_STMT(BPF_ALU + BPF_ADD + BPF_K, 11),
BPF_STMT(BPF_ALU + BPF_SUB + BPF_K, 5),
BPF_STMT(BPF_ALU + BPF_MUL + BPF_K, 2),
BPF_STMT(BPF_ALU + BPF_DIV + BPF_K, 10),
BPF_STMT(BPF_ALU + BPF_OR + BPF_K, 2),
BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 1),
BPF_STMT(BPF_ALU + BPF_LSH + BPF_K, 4),
BPF_STMT(BPF_ALU + BPF_RSH + BPF_K, 1),
BPF_STMT(BPF_ALU + BPF_XOR + BPF_K, 17),
BPF_STMT(BPF_LDX + BPF_IMM, 2),
BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 1),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 27, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL),
};
SAPI_ASSERT_OK_AND_ASSIGN(uint32_t result, Evaluate(prog, {.nr = 1}));
EXPECT_THAT(result, Eq(SECCOMP_RET_ALLOW));
SAPI_ASSERT_OK_AND_ASSIGN(result, Evaluate(prog, {.nr = 2}));
EXPECT_THAT(result, Eq(SECCOMP_RET_KILL));
}
TEST(EvaluatorTest, InvalidDivision) {
sock_filter prog[] = {
BPF_STMT(BPF_LD + BPF_IMM, 1),
BPF_STMT(BPF_ALU + BPF_DIV + BPF_K, 0),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
};
EXPECT_THAT(Evaluate(prog, {}),
sapi::StatusIs(absl::StatusCode::kInvalidArgument));
}
TEST(EvaluatorTest, InvalidAluOp) {
sock_filter prog[] = {
BPF_STMT(BPF_LD + BPF_IMM, 1),
BPF_STMT(BPF_ALU + 0xe0 + BPF_K, 10),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
};
EXPECT_THAT(Evaluate(prog, {}),
sapi::StatusIs(absl::StatusCode::kInvalidArgument,
"Invalid instruction 228"));
}
TEST(EvaluatorTest, InvalidJump) {
sock_filter prog[] = {
BPF_STMT(BPF_LD + BPF_IMM, 1),
BPF_JUMP(BPF_JMP + 0xe0 + BPF_K, 1, 0, 0),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
};
EXPECT_THAT(Evaluate(prog, {}),
sapi::StatusIs(absl::StatusCode::kInvalidArgument,
"Invalid instruction 229"));
}
TEST(EvaluatorTest, InvalidInst) {
sock_filter prog[] = {
BPF_STMT(BPF_ST + BPF_X, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
};
EXPECT_THAT(Evaluate(prog, {}),
sapi::StatusIs(absl::StatusCode::kInvalidArgument,
"Invalid instruction 10"));
}
TEST(EvaluatorTest, EmptyProgram) {
EXPECT_THAT(Evaluate({}, {.nr = 1}),
sapi::StatusIs(absl::StatusCode::kInvalidArgument,
"Out of bounds execution"));
}
TEST(EvaluatorTest, NoReturn) {
sock_filter prog[] = {
LOAD_SYSCALL_NR,
};
EXPECT_THAT(Evaluate(prog, {.nr = 1}),
sapi::StatusIs(absl::StatusCode::kInvalidArgument,
"Fall through to out of bounds execution"));
}
TEST(EvaluatorTest, OutOfBoundsJump) {
sock_filter prog[] = {
LOAD_SYSCALL_NR,
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 1, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
};
EXPECT_THAT(
Evaluate(prog, {.nr = 2}),
sapi::StatusIs(absl::StatusCode::kInvalidArgument, "Out of bounds jump"));
}
TEST(EvaluatorTest, OutOfMemoryOps) {
std::vector<std::vector<sock_filter>> progs = {
{
BPF_STMT(BPF_LD + BPF_IMM, 1),
BPF_STMT(BPF_ST, 17),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
},
{
BPF_STMT(BPF_LDX + BPF_IMM, 1),
BPF_STMT(BPF_STX, 17),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
},
{
BPF_STMT(BPF_LD + BPF_MEM, 17),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
},
{
BPF_STMT(BPF_LDX + BPF_MEM, 17),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
},
};
for (const std::vector<sock_filter>& prog : progs) {
EXPECT_THAT(Evaluate(prog, {}),
sapi::StatusIs(absl::StatusCode::kInvalidArgument));
}
}
TEST(EvaluatorTest, MisalignedLoad) {
sock_filter prog[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 3),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
};
EXPECT_THAT(Evaluate(prog, {}),
sapi::StatusIs(absl::StatusCode::kInvalidArgument,
"Misaligned read (3)"));
}
TEST(EvaluatorTest, OutOfBoundsLoad) {
sock_filter prog[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 4096),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
};
EXPECT_THAT(Evaluate(prog, {}),
sapi::StatusIs(absl::StatusCode::kInvalidArgument,
"Out of bounds read (4096)"));
}
} // namespace
} // namespace sandbox2::bpf