From e3b2d232b4d12d297255aad4173beea9984741ad Mon Sep 17 00:00:00 2001 From: Wiktor Garbacz Date: Tue, 7 Mar 2023 05:03:22 -0800 Subject: [PATCH] Add test for bpf disassembler Also always handle the new return values. PiperOrigin-RevId: 514698931 Change-Id: Ib4ce06e4f17c438271a0452053d3b0bc368e9970 --- sandboxed_api/sandbox2/BUILD.bazel | 11 ++ sandboxed_api/sandbox2/CMakeLists.txt | 25 ++- sandboxed_api/sandbox2/bpfdisassembler.cc | 30 ++-- .../sandbox2/bpfdisassembler_test.cc | 149 ++++++++++++++++++ 4 files changed, 199 insertions(+), 16 deletions(-) create mode 100644 sandboxed_api/sandbox2/bpfdisassembler_test.cc diff --git a/sandboxed_api/sandbox2/BUILD.bazel b/sandboxed_api/sandbox2/BUILD.bazel index cd6dad5..7e76264 100644 --- a/sandboxed_api/sandbox2/BUILD.bazel +++ b/sandboxed_api/sandbox2/BUILD.bazel @@ -1020,3 +1020,14 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "bpfdisassembler_test", + srcs = ["bpfdisassembler_test.cc"], + copts = sapi_platform_copts(), + deps = [ + ":bpfdisassembler", + "//sandboxed_api/sandbox2/util:bpf_helper", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/sandboxed_api/sandbox2/CMakeLists.txt b/sandboxed_api/sandbox2/CMakeLists.txt index 9f3a9a2..86bf2b8 100644 --- a/sandboxed_api/sandbox2/CMakeLists.txt +++ b/sandboxed_api/sandbox2/CMakeLists.txt @@ -32,10 +32,10 @@ add_library(sandbox2_bpfdisassembler ${SAPI_LIB_TYPE} bpfdisassembler.h ) add_library(sandbox2::bpfdisassembler ALIAS sandbox2_bpfdisassembler) -target_link_libraries(sandbox2_bpfdisassembler PRIVATE - absl::span - absl::strings - sapi::base +target_link_libraries(sandbox2_bpfdisassembler + PUBLIC absl::span + PRIVATE absl::strings + sapi::base ) # sandboxed_api/sandbox2:regs @@ -1121,6 +1121,23 @@ if(BUILD_TESTING AND SAPI_BUILD_TESTING) ENVIRONMENT "TEST_TMPDIR=/tmp" ENVIRONMENT "TEST_SRCDIR=${PROJECT_BINARY_DIR}" ) + + # sandboxed_api/sandbox2:bpfdisassembler_test + add_executable(sandbox2_bpfdisassembler_test + bpfdisassembler_test.cc + ) + set_target_properties(sandbox2_bpfdisassembler_test PROPERTIES + OUTPUT_NAME bpfdisassembler_test + ) + target_link_libraries(sandbox2_bpfdisassembler_test + PRIVATE sandbox2::bpfdisassembler + sandbox2::bpf_helper + sapi::test_main + ) + gtest_discover_tests_xcompile(sandbox2_bpfdisassembler_test PROPERTIES + ENVIRONMENT "TEST_TMPDIR=/tmp" + ENVIRONMENT "TEST_SRCDIR=${PROJECT_BINARY_DIR}" + ) endif() configure_file( diff --git a/sandboxed_api/sandbox2/bpfdisassembler.cc b/sandboxed_api/sandbox2/bpfdisassembler.cc index eebb282..94d101a 100644 --- a/sandboxed_api/sandbox2/bpfdisassembler.cc +++ b/sandboxed_api/sandbox2/bpfdisassembler.cc @@ -27,6 +27,22 @@ (what) >= offsetof(seccomp_data, field)) && \ ((what) < (offsetof(seccomp_data, field) + sizeof(seccomp_data::field)))) +#ifndef SECCOMP_RET_USER_NOTIF +#define SECCOMP_RET_USER_NOTIF 0x7fc00000U /* notifies userspace */ +#endif + +#ifndef SECCOMP_RET_LOG +#define SECCOMP_RET_LOG 0x7ffc0000U /* allow after logging */ +#endif + +#ifndef SECCOMP_RET_KILL_PROCESS +#define SECCOMP_RET_KILL_PROCESS 0x80000000U /* kill the process */ +#endif + +#ifndef SECCOMP_RET_ACTION_FULL +#define SECCOMP_RET_ACTION_FULL 0xffff0000U +#endif + namespace sandbox2 { namespace bpf { namespace { @@ -94,8 +110,8 @@ std::string DecodeInstruction(const sock_filter& inst, int pc) { switch (inst.code) { case BPF_LD | BPF_W | BPF_ABS: if (inst.k & 3) { - return absl::StrCat("A := *0x", absl::Hex(inst.k), - " (misaligned read)"); + return absl::StrCat("A := data[0x", absl::Hex(inst.k), + "] (misaligned load)"); } if (INSIDE_FIELD(inst.k, nr)) { return "A := syscall number"; @@ -142,23 +158,13 @@ std::string DecodeInstruction(const sock_filter& inst, int pc) { return absl::StrCat("M[", inst.k, "] := X"); case BPF_RET | BPF_K: { __u32 data = inst.k & SECCOMP_RET_DATA; -#ifdef SECCOMP_RET_ACTION_FULL switch (inst.k & SECCOMP_RET_ACTION_FULL) { -#ifdef SECCOMP_RET_KILL_PROCESS case SECCOMP_RET_KILL_PROCESS: return "KILL_PROCESS"; -#endif -#else - switch (inst.k & SECCOMP_RET_ACTION) { -#endif -#ifdef SECCOMP_RET_LOG case SECCOMP_RET_LOG: return "LOG"; -#endif -#ifdef SECCOMP_RET_USER_NOTIF case SECCOMP_RET_USER_NOTIF: return "USER_NOTIF"; -#endif case SECCOMP_RET_KILL: return "KILL"; case SECCOMP_RET_ALLOW: diff --git a/sandboxed_api/sandbox2/bpfdisassembler_test.cc b/sandboxed_api/sandbox2/bpfdisassembler_test.cc new file mode 100644 index 0000000..3960bb4 --- /dev/null +++ b/sandboxed_api/sandbox2/bpfdisassembler_test.cc @@ -0,0 +1,149 @@ +#include "sandboxed_api/sandbox2/bpfdisassembler.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "sandboxed_api/sandbox2/util/bpf_helper.h" + +namespace sandbox2 { +namespace bpf { +namespace { + +using ::testing::Eq; +using ::testing::StartsWith; + +#ifndef SECCOMP_RET_USER_NOTIF +#define SECCOMP_RET_USER_NOTIF 0x7fc00000U /* notifies userspace */ +#endif + +#ifndef SECCOMP_RET_KILL_PROCESS +#define SECCOMP_RET_KILL_PROCESS 0x80000000U /* kill the process */ +#endif + +#ifndef SECCOMP_RET_LOG +#define SECCOMP_RET_LOG 0x7ffc0000U /* allow after logging */ +#endif + +TEST(DecodeInstructionTest, Loads) { + EXPECT_THAT(DecodeInstruction(LOAD_ARCH, 1), Eq("A := architecture")); + EXPECT_THAT(DecodeInstruction(LOAD_SYSCALL_NR, 1), Eq("A := syscall number")); + EXPECT_THAT(DecodeInstruction(ARG_32(0), 1), Eq("A := arg 0 low")); + EXPECT_THAT( + DecodeInstruction(BPF_STMT(BPF_LD | BPF_W | BPF_ABS, HI_ARG(0)), 1), + Eq("A := arg 0 high")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_LD | BPF_W | BPF_LEN, 0), 1), + Eq("A := sizeof(seccomp_data)")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_LDX | BPF_W | BPF_LEN, 0), 1), + Eq("X := sizeof(seccomp_data)")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_LD | BPF_IMM, 0x1234), 1), + Eq("A := 0x1234")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_LDX | BPF_IMM, 0x1234), 1), + Eq("X := 0x1234")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_MISC | BPF_TAX, 0), 1), + Eq("X := A")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_MISC | BPF_TXA, 0), 1), + Eq("A := X")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 0x1), 1), + Eq("A := data[0x1] (misaligned load)")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 0x1234), 1), + Eq("A := data[0x1234] (invalid load)")); +} + +TEST(DecodeInstructionTest, Memory) { + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_ST, 1), 1), Eq("M[1] := A")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_STX, 1), 1), Eq("M[1] := X")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_LD | BPF_MEM, 1), 1), + Eq("A := M[1]")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_LDX | BPF_MEM, 1), 1), + Eq("X := M[1]")); +} + +TEST(DecodeInstructionTest, Returns) { + EXPECT_THAT(DecodeInstruction(KILL, 1), Eq("KILL")); + EXPECT_THAT(DecodeInstruction(ALLOW, 1), Eq("ALLOW")); + EXPECT_THAT(DecodeInstruction(TRAP(0x12), 1), Eq("TRAP 0x12")); + EXPECT_THAT(DecodeInstruction(ERRNO(0x23), 1), Eq("ERRNO 0x23")); + EXPECT_THAT(DecodeInstruction(TRACE(0x34), 1), Eq("TRACE 0x34")); + EXPECT_THAT( + DecodeInstruction(BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_USER_NOTIF), 1), + Eq("USER_NOTIF")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_LOG), 1), + Eq("LOG")); + EXPECT_THAT( + DecodeInstruction(BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL_PROCESS), 1), + Eq("KILL_PROCESS")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_RET + BPF_A, 0), 1), + Eq("return A")); +} + +TEST(DecodeInstructionTest, Alu) { + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_ALU | BPF_NEG, 0), 1), + Eq("A := -A")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 5), 1), + Eq("A := A + 0x5")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_ALU | BPF_SUB | BPF_K, 5), 1), + Eq("A := A - 0x5")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_ALU | BPF_DIV | BPF_X, 0), 1), + Eq("A := A / X")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_ALU | BPF_MUL | BPF_X, 0), 1), + Eq("A := A * X")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 6), 1), + Eq("A := A & 0x6")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_ALU | BPF_OR | BPF_K, 7), 1), + Eq("A := A | 0x7")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_ALU | BPF_XOR | BPF_K, 8), 1), + Eq("A := A ^ 0x8")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_ALU | BPF_RSH | BPF_K, 9), 1), + Eq("A := A >> 0x9")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 1), 1), + Eq("A := A << 0x1")); +} + +TEST(DecodeInstructionTest, Jump) { + EXPECT_THAT( + DecodeInstruction(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x1234, 1, 0), 1), + Eq("if A == 0x1234 goto 3")); + EXPECT_THAT( + DecodeInstruction(BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K, 0x1234, 0, 1), 1), + Eq("if A <= 0x1234 goto 3")); + EXPECT_THAT( + DecodeInstruction(BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K, 0x1234, 1, 2), 1), + Eq("if A > 0x1234 then 3 else 4")); + EXPECT_THAT( + DecodeInstruction(BPF_JUMP(BPF_JMP | BPF_JSET | BPF_X, 1, 1, 0), 1), + Eq("if A & X goto 3")); + EXPECT_THAT( + DecodeInstruction(BPF_JUMP(BPF_JMP | BPF_JGE | BPF_X, 0, 0, 1), 1), + Eq("if A < X goto 3")); + EXPECT_THAT( + DecodeInstruction(BPF_JUMP(BPF_JMP | BPF_JGE | BPF_X, 0, 1, 2), 1), + Eq("if A >= X then 3 else 4")); + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_JMP | BPF_K, 3), 1), + Eq("jump to 5")); +} + +TEST(DecodeInstructionTest, Invalid) { + EXPECT_THAT(DecodeInstruction(BPF_STMT(BPF_LDX | BPF_W | BPF_ABS, 0), 1), + StartsWith("Invalid instruction")); +} + +TEST(DisasmTest, Simple) { + EXPECT_THAT(Disasm({ALLOW}), Eq("000: ALLOW\n")); + EXPECT_THAT(Disasm({KILL}), Eq("000: KILL\n")); +} + +TEST(DisasmTest, Complex) { + EXPECT_THAT(Disasm({LOAD_ARCH, JNE32(0x1, KILL), LOAD_SYSCALL_NR, + JEQ32(0x1234, ERRNO(0x33)), TRACE(0x22)}), + Eq(R"(000: A := architecture +001: if A == 0x1 goto 3 +002: KILL +003: A := syscall number +004: if A != 0x1234 goto 6 +005: ERRNO 0x33 +006: TRACE 0x22 +)")); +} + +} // namespace +} // namespace bpf +} // namespace sandbox2