/* Copyright 2016 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 * Test syscall filtering using gtest.
 */

#include <asm/unistd.h>
#include <errno.h>
#include <fcntl.h> /* For O_WRONLY. */

#include <gtest/gtest.h>
#include <span>
#include <string>

#include "bpf.h"
#include "syscall_filter.h"
#include "syscall_filter_unittest_macros.h"
#include "test_util.h"
#include "util.h"

namespace {

// Simple C++ -> C wrappers to simplify test code.

enum ret_trap {
  USE_RET_KILL = 0,
  USE_RET_TRAP = 1,
};

enum use_logging {
  NO_LOGGING = 0,
  USE_SIGSYS_LOGGING = 1,
  USE_RET_LOG_LOGGING = 2,
};

int test_compile_filter(const std::string& filename,
                        FILE* policy_file,
                        struct sock_fprog* prog,
                        enum block_action action = ACTION_RET_KILL,
                        enum use_logging allow_logging = NO_LOGGING,
                        bool allow_dup_syscalls = true,
                        bool include_libc_compatibility_allowlist = false) {
  struct filter_options filteropts {
    .action = action, .allow_logging = allow_logging != NO_LOGGING,
    .allow_syscalls_for_logging = allow_logging == USE_SIGSYS_LOGGING,
    .allow_duplicate_syscalls = allow_dup_syscalls,
    .include_libc_compatibility_allowlist =
        include_libc_compatibility_allowlist,
  };
  return compile_filter(filename.c_str(), policy_file, prog, &filteropts);
}

int test_compile_file(std::string filename,
                      FILE* policy_file,
                      struct filter_block* head,
                      struct filter_block** arg_blocks,
                      struct bpf_labels* labels,
                      enum block_action action = ACTION_RET_KILL,
                      enum use_logging allow_logging = NO_LOGGING,
                      unsigned int include_level = 0,
                      bool allow_dup_syscalls = false) {
  struct filter_options filteropts {
    .action = action, .allow_logging = allow_logging != NO_LOGGING,
    .allow_syscalls_for_logging = allow_logging == USE_SIGSYS_LOGGING,
    .allow_duplicate_syscalls = allow_dup_syscalls,
    .include_libc_compatibility_allowlist = false,
  };
  size_t num_syscalls = get_num_syscalls();
  struct parser_state** previous_syscalls =
      (struct parser_state**)calloc(num_syscalls, sizeof(struct parser_state*));
  int res = compile_file(filename.c_str(), policy_file, head, arg_blocks,
                         labels, &filteropts, previous_syscalls, include_level);
  free_previous_syscalls(previous_syscalls);
  return res;
}

struct filter_block* test_compile_policy_line(
    struct parser_state* state,
    int nr,
    const std::string& policy_line,
    unsigned int label_id,
    struct bpf_labels* labels,
    enum block_action action = ACTION_RET_KILL) {
  return compile_policy_line(state, nr, policy_line.c_str(), label_id, labels,
                             action);
}

std::span<const char *const> get_libc_compatibility_allowlist() {
  return std::span(libc_compatibility_syscalls,
                   libc_compatibility_syscalls_len);
}
}  // namespace

/* Test that setting one BPF instruction works. */
TEST(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(bpf, bpf_load_arg) {
  struct sock_filter load_arg[BPF_LOAD_ARG_LEN];
  const int argidx = 1;
  size_t len = bpf_load_arg(load_arg, argidx);

  EXPECT_EQ(len, BPF_LOAD_ARG_LEN);

#if defined(BITS32)
  EXPECT_EQ_STMT(&load_arg[0], BPF_LD + BPF_W + BPF_ABS, LO_ARG(argidx));
#elif defined(BITS64)
  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);
#endif
}

TEST(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_jeq(comp_jeq, c, jt, jf);

  EXPECT_EQ(len, BPF_COMP_LEN);

#if defined(BITS32)
  EXPECT_EQ_BLOCK(&comp_jeq[0], BPF_JMP + BPF_JEQ + BPF_K, c, jt, jf);
#elif defined(BITS64)
  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);
#endif
}

TEST(bpf, bpf_comp_jset) {
  struct sock_filter comp_jset[BPF_COMP_LEN];
  unsigned long mask = (1UL << (sizeof(unsigned long) * 8 - 1)) | O_WRONLY;
  unsigned char jt = 1;
  unsigned char jf = 2;

  size_t len = bpf_comp_jset(comp_jset, mask, jt, jf);

  EXPECT_EQ(len, BPF_COMP_LEN);

#if defined(BITS32)
  EXPECT_EQ_BLOCK(&comp_jset[0], BPF_JMP + BPF_JSET + BPF_K, mask, jt, jf);
#elif defined(BITS64)
  EXPECT_EQ_BLOCK(&comp_jset[0], BPF_JMP + BPF_JSET + BPF_K, 0x80000000, jt + 2,
                  0);
  EXPECT_EQ_STMT(&comp_jset[1], BPF_LD + BPF_MEM, 0);
  EXPECT_EQ_BLOCK(&comp_jset[2], BPF_JMP + BPF_JSET + BPF_K, O_WRONLY, jt, jf);
#endif
}

TEST(bpf, bpf_comp_jin) {
  struct sock_filter comp_jin[BPF_COMP_LEN];
  unsigned long mask = (1UL << (sizeof(unsigned long) * 8 - 1)) | O_WRONLY;
  unsigned char jt = 10;
  unsigned char jf = 20;

  size_t len = bpf_comp_jin(comp_jin, mask, jt, jf);

  EXPECT_EQ(len, BPF_COMP_LEN);

#if defined(BITS32)
  EXPECT_EQ_BLOCK(&comp_jin[0], BPF_JMP + BPF_JSET + BPF_K, ~mask, jf, jt);
#elif defined(BITS64)
  EXPECT_EQ_BLOCK(&comp_jin[0], BPF_JMP + BPF_JSET + BPF_K, 0x7FFFFFFF, jf + 2,
                  0);
  EXPECT_EQ_STMT(&comp_jin[1], BPF_LD + BPF_MEM, 0);
  EXPECT_EQ_BLOCK(&comp_jin[2], BPF_JMP + BPF_JSET + BPF_K, ~O_WRONLY, jf, jt);
#endif
}

TEST(bpf, bpf_arg_comp) {
  struct sock_filter* arg_comp;
  int op = EQ;
  const 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);

#if defined(BITS32)
  EXPECT_EQ_STMT(&arg_comp[0], BPF_LD + BPF_W + BPF_ABS, LO_ARG(argidx));
  EXPECT_EQ_BLOCK(&arg_comp[1], BPF_JMP + BPF_JEQ + BPF_K, c, 1, 0);
  EXPECT_JUMP_LBL(&arg_comp[2]);
#elif defined(BITS64)
  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]);
#endif
  free(arg_comp);
}

TEST(bpf, bpf_arg_comp_invalid) {
  struct sock_filter* arg_comp;
  int op = MIN_OPERATOR + 32;
  const 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, 0);
  free(arg_comp);
}

TEST(bpf, bpf_validate_arch) {
  struct sock_filter validate_arch[ARCH_VALIDATION_LEN];

  size_t len = bpf_validate_arch(validate_arch);

  EXPECT_EQ(len, ARCH_VALIDATION_LEN);
  EXPECT_ARCH_VALIDATION(validate_arch);
}

TEST(bpf, bpf_allow_syscall) {
  struct sock_filter allow_syscall[ALLOW_SYSCALL_LEN];
  int nr = 1;

  size_t len = bpf_allow_syscall(allow_syscall, nr);

  EXPECT_EQ(len, ALLOW_SYSCALL_LEN);
  EXPECT_ALLOW_SYSCALL(allow_syscall, nr);
}

TEST(bpf, bpf_allow_syscall_args) {
  struct sock_filter allow_syscall[ALLOW_SYSCALL_LEN];
  int nr = 1;
  unsigned int id = 1024;

  size_t len = bpf_allow_syscall_args(allow_syscall, nr, id);

  EXPECT_EQ(len, ALLOW_SYSCALL_LEN);
  EXPECT_ALLOW_SYSCALL_ARGS(allow_syscall, nr, id, JUMP_JT, JUMP_JF);
}

class BpfLabelTest : public ::testing::Test {
 protected:
  virtual void SetUp() { labels_.count = 0; }
  virtual void TearDown() { free_label_strings(&labels_); }
  struct bpf_labels labels_;
};

TEST_F(BpfLabelTest, zero_length_filter) {
  int res = bpf_resolve_jumps(&labels_, nullptr, 0);

  EXPECT_EQ(res, 0);
  EXPECT_EQ(labels_.count, 0U);
}

TEST_F(BpfLabelTest, single_label) {
  struct sock_filter test_label[1];

  int id = bpf_label_id(&labels_, "test");
  set_bpf_lbl(test_label, id);
  int res = bpf_resolve_jumps(&labels_, test_label, 1);

  EXPECT_EQ(res, 0);
  EXPECT_EQ(labels_.count, 1U);
}

TEST_F(BpfLabelTest, repeated_label) {
  struct sock_filter test_label[2];

  int id = bpf_label_id(&labels_, "test");
  set_bpf_lbl(&test_label[0], id);
  set_bpf_lbl(&test_label[1], id);
  int res = bpf_resolve_jumps(&labels_, test_label, 2);

  EXPECT_EQ(res, -1);
}

TEST_F(BpfLabelTest, jump_with_no_label) {
  struct sock_filter test_jump[1];

  set_bpf_jump_lbl(test_jump, 14831);
  int res = bpf_resolve_jumps(&labels_, test_jump, 1);

  EXPECT_EQ(res, -1);
}

TEST_F(BpfLabelTest, jump_to_valid_label) {
  struct sock_filter test_jump[2];

  int id = bpf_label_id(&labels_, "test");
  set_bpf_jump_lbl(&test_jump[0], id);
  set_bpf_lbl(&test_jump[1], id);

  int res = bpf_resolve_jumps(&labels_, test_jump, 2);
  EXPECT_EQ(res, 0);
  EXPECT_EQ(labels_.count, 1U);
}

TEST_F(BpfLabelTest, jump_to_invalid_label) {
  struct sock_filter test_jump[2];

  int id = bpf_label_id(&labels_, "test");
  set_bpf_jump_lbl(&test_jump[0], id + 1);
  set_bpf_lbl(&test_jump[1], id);

  int res = bpf_resolve_jumps(&labels_, test_jump, 2);
  EXPECT_EQ(res, -1);
}

TEST_F(BpfLabelTest, jump_to_unresolved_label) {
  struct sock_filter test_jump[2];

  int id = bpf_label_id(&labels_, "test");
  /* Notice the order of the instructions is reversed. */
  set_bpf_lbl(&test_jump[0], id);
  set_bpf_jump_lbl(&test_jump[1], id);

  int res = bpf_resolve_jumps(&labels_, test_jump, 2);
  EXPECT_EQ(res, -1);
}

TEST_F(BpfLabelTest, too_many_labels) {
  unsigned int i;
  char label[20];

  for (i = 0; i < BPF_LABELS_MAX; i++) {
    snprintf(label, 20, "test%u", i);
    (void)bpf_label_id(&labels_, label);
  }
  int id = bpf_label_id(&labels_, "test");

  /* Insertion failed... */
  EXPECT_EQ(id, -1);
  /* ... because the label lookup table is full. */
  EXPECT_EQ(labels_.count, BPF_LABELS_MAX);
}

class ArgFilterTest : public ::testing::Test {
 protected:
  virtual void SetUp() {
    labels_.count = 0;
    state_.filename = "policy";
    state_.line_number = 1;
  }
  virtual void TearDown() { free_label_strings(&labels_); }
  struct bpf_labels labels_;
  int nr_ = 1;
  unsigned int id_ = 0;
  struct parser_state state_;
};

TEST_F(ArgFilterTest, empty_atom) {
  std::string fragment = "";

  struct filter_block* block =
      test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);
  ASSERT_EQ(block, nullptr);
}

TEST_F(ArgFilterTest, whitespace_atom) {
  std::string fragment = "\t    ";

  struct filter_block* block =
      test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);
  ASSERT_EQ(block, nullptr);
}

TEST_F(ArgFilterTest, no_comparison) {
  std::string fragment = "arg0";

  struct filter_block* block =
      test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);
  ASSERT_EQ(block, nullptr);
}

TEST_F(ArgFilterTest, no_constant) {
  std::string fragment = "arg0 ==";

  struct filter_block* block =
      test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);
  ASSERT_EQ(block, nullptr);
}

TEST_F(ArgFilterTest, arg0_equals) {
  std::string fragment = "arg0 == 0";

  struct filter_block* block =
      test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);

  ASSERT_NE(block, nullptr);
  size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2;
  EXPECT_EQ(block->total_len, exp_total_len);

  /* First block is a label. */
  struct filter_block* curr_block = block;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_EQ(curr_block->len, 1U);
  EXPECT_LBL(curr_block->instrs);

  /* Second block is a comparison. */
  curr_block = curr_block->next;
  EXPECT_COMP(curr_block);

  /* Third block is a jump and a label (end of AND group). */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_GROUP_END(curr_block);

  /* Fourth block is SECCOMP_RET_KILL. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_KILL(curr_block);

  /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW(curr_block);

  EXPECT_EQ(curr_block->next, nullptr);

  free_block_list(block);
}

TEST_F(ArgFilterTest, arg0_equals_trap) {
  std::string fragment = "arg0 == 0";

  struct filter_block* block = test_compile_policy_line(
      &state_, nr_, fragment, id_, &labels_, ACTION_RET_TRAP);

  ASSERT_NE(block, nullptr);
  size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2;
  EXPECT_EQ(block->total_len, exp_total_len);

  /* First block is a label. */
  struct filter_block* curr_block = block;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_EQ(curr_block->len, 1U);
  EXPECT_LBL(curr_block->instrs);

  /* Second block is a comparison. */
  curr_block = curr_block->next;
  EXPECT_COMP(curr_block);

  /* Third block is a jump and a label (end of AND group). */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_GROUP_END(curr_block);

  /* Fourth block is SECCOMP_RET_TRAP. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_TRAP(curr_block);

  /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW(curr_block);

  EXPECT_EQ(curr_block->next, nullptr);

  free_block_list(block);
}

TEST_F(ArgFilterTest, arg0_equals_log) {
  std::string fragment = "arg0 == 0";

  struct filter_block* block = test_compile_policy_line(
      &state_, nr_, fragment, id_, &labels_, ACTION_RET_LOG);

  ASSERT_NE(block, nullptr);
  size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2;
  EXPECT_EQ(block->total_len, exp_total_len);

  /* First block is a label. */
  struct filter_block* curr_block = block;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_EQ(curr_block->len, 1U);
  EXPECT_LBL(curr_block->instrs);

  /* Second block is a comparison. */
  curr_block = curr_block->next;
  EXPECT_COMP(curr_block);

  /* Third block is a jump and a label (end of AND group). */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_GROUP_END(curr_block);

  /* Fourth block is SECCOMP_RET_LOG. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_LOG(curr_block);

  /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW(curr_block);

  EXPECT_EQ(curr_block->next, nullptr);

  free_block_list(block);
}

TEST_F(ArgFilterTest, arg0_short_gt_ge_comparisons) {
  for (const std::string fragment :
       {"arg1 < 0xff", "arg1 <= 0xff", "arg1 > 0xff", "arg1 >= 0xff"}) {
    struct filter_block* block =
        test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);

    ASSERT_NE(block, nullptr);
    size_t exp_total_len = 1 + (BPF_ARG_SHORT_GT_GE_COMP_LEN + 1) + 2 + 1 + 2;
    EXPECT_EQ(block->total_len, exp_total_len);

    // First block is a label.
    struct filter_block* curr_block = block;
    ASSERT_NE(curr_block, nullptr);
    EXPECT_EQ(curr_block->len, 1U);
    EXPECT_LBL(curr_block->instrs);

    // Second block is a short gt/ge comparison.
    curr_block = curr_block->next;
    EXPECT_SHORT_GT_GE_COMP(curr_block);

    // Third block is a jump and a label (end of AND group).
    curr_block = curr_block->next;
    ASSERT_NE(curr_block, nullptr);
    EXPECT_GROUP_END(curr_block);

    // Fourth block is SECCOMP_RET_KILL.
    curr_block = curr_block->next;
    ASSERT_NE(curr_block, nullptr);
    EXPECT_KILL(curr_block);

    // Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW.
    curr_block = curr_block->next;
    ASSERT_NE(curr_block, nullptr);
    EXPECT_ALLOW(curr_block);

    EXPECT_EQ(curr_block->next, nullptr);

    free_block_list(block);
  }
}

#if defined(BITS64)
TEST_F(ArgFilterTest, arg0_long_gt_ge_comparisons) {
  for (const std::string fragment :
       {"arg1 < 0xbadc0ffee0ddf00d", "arg1 <= 0xbadc0ffee0ddf00d",
        "arg1 > 0xbadc0ffee0ddf00d", "arg1 >= 0xbadc0ffee0ddf00d"}) {
    struct filter_block* block =
        test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);

    ASSERT_NE(block, nullptr);
    size_t exp_total_len = 1 + (BPF_ARG_GT_GE_COMP_LEN + 1) + 2 + 1 + 2;
    EXPECT_EQ(block->total_len, exp_total_len);

    // First block is a label.
    struct filter_block* curr_block = block;
    ASSERT_NE(curr_block, nullptr);
    EXPECT_EQ(curr_block->len, 1U);
    EXPECT_LBL(curr_block->instrs);

    // Second block is a gt/ge comparison.
    curr_block = curr_block->next;
    EXPECT_GT_GE_COMP(curr_block);

    // Third block is a jump and a label (end of AND group).
    curr_block = curr_block->next;
    ASSERT_NE(curr_block, nullptr);
    EXPECT_GROUP_END(curr_block);

    // Fourth block is SECCOMP_RET_KILL.
    curr_block = curr_block->next;
    ASSERT_NE(curr_block, nullptr);
    EXPECT_KILL(curr_block);

    // Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW.
    curr_block = curr_block->next;
    ASSERT_NE(curr_block, nullptr);
    EXPECT_ALLOW(curr_block);

    EXPECT_EQ(curr_block->next, nullptr);

    free_block_list(block);
  }
}
#endif

TEST_F(ArgFilterTest, arg0_mask) {
  std::string fragment = "arg1 & O_RDWR";

  struct filter_block* block =
      test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);

  ASSERT_NE(block, nullptr);
  size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2;
  EXPECT_EQ(block->total_len, exp_total_len);

  /* First block is a label. */
  struct filter_block* curr_block = block;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_EQ(curr_block->len, 1U);
  EXPECT_LBL(curr_block->instrs);

  /* Second block is a comparison. */
  curr_block = curr_block->next;
  EXPECT_COMP(curr_block);

  /* Third block is a jump and a label (end of AND group). */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_GROUP_END(curr_block);

  /* Fourth block is SECCOMP_RET_KILL. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_KILL(curr_block);

  /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW(curr_block);

  EXPECT_EQ(curr_block->next, nullptr);

  free_block_list(block);
}

TEST_F(ArgFilterTest, arg0_flag_set_inclusion) {
  std::string fragment = "arg0 in O_RDONLY|O_CREAT";

  struct filter_block* block =
      test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);

  ASSERT_NE(block, nullptr);
  size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2;
  EXPECT_EQ(block->total_len, exp_total_len);

  /* First block is a label. */
  struct filter_block* curr_block = block;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_EQ(curr_block->len, 1U);
  EXPECT_LBL(curr_block->instrs);

  /* Second block is a comparison. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_COMP(curr_block);

  /* Third block is a jump and a label (end of AND group). */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_GROUP_END(curr_block);

  /* Fourth block is SECCOMP_RET_KILL. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_KILL(curr_block);

  /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW(curr_block);

  EXPECT_EQ(curr_block->next, nullptr);

  free_block_list(block);
}

TEST_F(ArgFilterTest, arg0_eq_mask) {
  std::string fragment = "arg1 == O_WRONLY|O_CREAT";

  struct filter_block* block =
      test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);

  ASSERT_NE(block, nullptr);
  size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2;
  EXPECT_EQ(block->total_len, exp_total_len);

  /* First block is a label. */
  struct filter_block* curr_block = block;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_EQ(curr_block->len, 1U);
  EXPECT_LBL(curr_block->instrs);

  /* Second block is a comparison. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_COMP(curr_block);
  EXPECT_EQ(curr_block->instrs[BPF_ARG_COMP_LEN - 1].k,
            (unsigned int)(O_WRONLY | O_CREAT));

  /* Third block is a jump and a label (end of AND group). */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_GROUP_END(curr_block);

  /* Fourth block is SECCOMP_RET_KILL. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_KILL(curr_block);

  /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW(curr_block);

  EXPECT_EQ(curr_block->next, nullptr);

  free_block_list(block);
}

TEST_F(ArgFilterTest, and_or) {
  std::string fragment = "arg0 == 0 && arg1 == 0 || arg0 == 1";

  struct filter_block* block =
      test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);
  ASSERT_NE(block, nullptr);
  size_t exp_total_len = 1 + 3 * (BPF_ARG_COMP_LEN + 1) + 2 + 2 + 1 + 2;
  EXPECT_EQ(block->total_len, exp_total_len);

  /* First block is a label. */
  struct filter_block* curr_block = block;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_EQ(curr_block->len, 1U);
  EXPECT_LBL(curr_block->instrs);

  /* Second block is a comparison ("arg0 == 0"). */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_COMP(curr_block);

  /* Third block is a comparison ("arg1 == 0"). */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_COMP(curr_block);

  /* Fourth block is a jump and a label (end of AND group). */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_GROUP_END(curr_block);

  /* Fifth block is a comparison ("arg0 == 1"). */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_COMP(curr_block);

  /* Sixth block is a jump and a label (end of AND group). */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_GROUP_END(curr_block);

  /* Seventh block is SECCOMP_RET_KILL. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_KILL(curr_block);

  /* Eigth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW(curr_block);

  EXPECT_EQ(curr_block->next, nullptr);

  free_block_list(block);
}

TEST_F(ArgFilterTest, ret_errno) {
  std::string fragment = "arg0 == 0 || arg0 == 1; return 1";

  struct filter_block* block =
      test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);
  ASSERT_NE(block, nullptr);
  size_t exp_total_len = 1 + 2 * (BPF_ARG_COMP_LEN + 1) + 2 + 2 + 1 + 2;
  EXPECT_EQ(block->total_len, exp_total_len);

  /* First block is a label. */
  struct filter_block* curr_block = block;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_EQ(curr_block->len, 1U);
  EXPECT_LBL(curr_block->instrs);

  /* Second block is a comparison ("arg0 == 0"). */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_COMP(curr_block);

  /* Third block is a jump and a label (end of AND group). */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_GROUP_END(curr_block);

  /* Fourth block is a comparison ("arg0 == 1"). */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_COMP(curr_block);

  /* Fifth block is a jump and a label (end of AND group). */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_GROUP_END(curr_block);

  /* Sixth block is SECCOMP_RET_ERRNO. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  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;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW(curr_block);

  EXPECT_EQ(curr_block->next, nullptr);

  free_block_list(block);
}

TEST_F(ArgFilterTest, unconditional_errno) {
  std::string fragment = "return 1";

  struct filter_block* block =
      test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);
  ASSERT_NE(block, nullptr);
  size_t exp_total_len = 2;
  EXPECT_EQ(block->total_len, exp_total_len);

  /* First block is a label. */
  struct filter_block* curr_block = block;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_EQ(curr_block->len, 1U);
  EXPECT_LBL(curr_block->instrs);

  /* Second block is SECCOMP_RET_ERRNO. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_EQ(curr_block->len, 1U);
  EXPECT_EQ_STMT(curr_block->instrs, BPF_RET + BPF_K,
                 SECCOMP_RET_ERRNO | (1 & SECCOMP_RET_DATA));

  EXPECT_EQ(curr_block->next, nullptr);

  free_block_list(block);
}

TEST_F(ArgFilterTest, invalid_arg_token) {
  std::string fragment = "org0 == 0";

  struct filter_block* block =
      test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);
  ASSERT_EQ(block, nullptr);
}

TEST_F(ArgFilterTest, invalid_arg_number) {
  std::string fragment = "argnn == 0";

  struct filter_block* block =
      test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);
  ASSERT_EQ(block, nullptr);
}

TEST_F(ArgFilterTest, extra_chars_in_arg_token) {
  std::string fragment = "arg0n == 0";

  struct filter_block* block =
      test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);
  ASSERT_EQ(block, nullptr);
}

TEST_F(ArgFilterTest, invalid_operator) {
  std::string fragment = "arg0 invalidop 0";

  struct filter_block* block =
      test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);
  ASSERT_EQ(block, nullptr);
}

TEST_F(ArgFilterTest, invalid_constant) {
  std::string fragment = "arg0 == INVALIDCONSTANT";

  struct filter_block* block =
      test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);
  ASSERT_EQ(block, nullptr);
}

TEST_F(ArgFilterTest, extra_tokens) {
  std::string fragment = "arg0 == 0 EXTRATOKEN";

  struct filter_block* block =
      test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);
  ASSERT_EQ(block, nullptr);
}

TEST_F(ArgFilterTest, invalid_errno) {
  std::string fragment = "arg0 == 0 && arg1 == 1; return errno";

  struct filter_block* block =
      test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);
  ASSERT_EQ(block, nullptr);
}

TEST_F(ArgFilterTest, log_no_ret_error) {
  std::string fragment = "arg0 == 0";

  struct filter_block* block = test_compile_policy_line(
      &state_, nr_, fragment, id_, &labels_, ACTION_RET_TRAP);

  ASSERT_NE(block, nullptr);
  size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2;
  EXPECT_EQ(block->total_len, exp_total_len);

  /* First block is a label. */
  struct filter_block* curr_block = block;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_EQ(curr_block->len, 1U);
  EXPECT_LBL(curr_block->instrs);

  /* Second block is a comparison. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_COMP(curr_block);

  /* Third block is a jump and a label (end of AND group). */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_GROUP_END(curr_block);

  /* Fourth block is SECCOMP_RET_TRAP, with no errno. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_TRAP(curr_block);

  /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW(curr_block);

  EXPECT_EQ(curr_block->next, nullptr);

  free_block_list(block);
}

TEST_F(ArgFilterTest, log_bad_ret_error) {
  std::string fragment = "arg0 == 0; return";

  struct filter_block* block =
      test_compile_policy_line(&state_, nr_, fragment, id_, &labels_);
  ASSERT_NE(block, nullptr);
  size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2;
  EXPECT_EQ(block->total_len, exp_total_len);

  /* First block is a label. */
  struct filter_block* curr_block = block;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_EQ(curr_block->len, 1U);
  EXPECT_LBL(curr_block->instrs);

  /* Second block is a comparison ("arg0 == 0"). */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_COMP(curr_block);

  /* Third block is a jump and a label (end of AND group). */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_GROUP_END(curr_block);

  /*
   * Sixth block is NOT SECCOMP_RET_ERRNO, it should be SECCOMP_RET_KILL.
   */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_KILL(curr_block);

  /* Seventh block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW(curr_block);

  EXPECT_EQ(curr_block->next, nullptr);

  free_block_list(block);
}

TEST_F(ArgFilterTest, no_log_bad_ret_error) {
  std::string fragment = "arg0 == 0; return";

  struct filter_block* block = test_compile_policy_line(
      &state_, nr_, fragment, id_, &labels_, ACTION_RET_TRAP);
  ASSERT_NE(block, nullptr);
  size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2;
  EXPECT_EQ(block->total_len, exp_total_len);

  /* First block is a label. */
  struct filter_block* curr_block = block;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_EQ(curr_block->len, 1U);
  EXPECT_LBL(curr_block->instrs);

  /* Second block is a comparison ("arg0 == 0"). */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_COMP(curr_block);

  /* Third block is a jump and a label (end of AND group). */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_GROUP_END(curr_block);

  /*
   * Sixth block is *not* SECCOMP_RET_ERRNO, it should be
   * SECCOMP_RET_TRAP.
   */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_TRAP(curr_block);

  /* Seventh block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW(curr_block);

  EXPECT_EQ(curr_block->next, nullptr);

  free_block_list(block);
}

namespace {

class FileTest : public ::testing::Test {
 protected:
  virtual void SetUp() {
    labels_.count = 0;
    head_ = new_filter_block();
    arg_blocks_ = nullptr;
  }
  virtual void TearDown() {
    free_label_strings(&labels_);
    free_block_list(head_);
    free_block_list(arg_blocks_);
  }
  struct bpf_labels labels_;
  struct filter_block* head_;
  struct filter_block* arg_blocks_;
};

}  // namespace

TEST_F(FileTest, malformed_policy) {
  std::string policy = "malformed";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);
  int res =
      test_compile_file("policy", policy_file, head_, &arg_blocks_, &labels_);
  fclose(policy_file);

  /*
   * Policy is malformed, but process should not crash.
   */
  ASSERT_EQ(res, -1);
}

TEST_F(FileTest, double_free_on_compile_error) {
  std::string policy =
      "read:arg0 == 0\n"
      "write:0";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);
  int res =
      test_compile_file("policy", policy_file, head_, &arg_blocks_, &labels_);
  fclose(policy_file);

  /*
   * Policy is malformed, but process should not crash.
   */
  ASSERT_EQ(res, -1);
}

TEST_F(FileTest, invalid_return) {
  std::string policy = "read:arg0 == 0; ;";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);
  int res =
      test_compile_file("policy", policy_file, head_, &arg_blocks_, &labels_);
  fclose(policy_file);

  /*
   * Policy is malformed, but process should not crash.
   */
  ASSERT_EQ(res, -1);
}

TEST_F(FileTest, seccomp_global_allowlist) {
  std::string policy = "";
  FILE *policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  struct sock_fprog actual;
  int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_KILL,
        NO_LOGGING, false /* allow_dup_syscalls */,
        true /* include_libc_compatibility_allowlist */);
  fclose(policy_file);

  ASSERT_EQ(res, 0);
  std::span<const char *const> allowlist = get_libc_compatibility_allowlist();
  size_t offset_after_allowlist =
    ARCH_VALIDATION_LEN + 1 + 2 * allowlist.size();
  EXPECT_EQ(actual.len, offset_after_allowlist + 1);
  EXPECT_ARCH_VALIDATION(actual.filter);
  EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN,
                 BPF_LD + BPF_W + BPF_ABS, syscall_nr);
  for (size_t i = 0; i < allowlist.size(); ++i) {
    int allowlist_entry_nr = lookup_syscall(allowlist[i], nullptr);
    EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 1 + 2 * i,
                         allowlist_entry_nr);
  }
  EXPECT_EQ_STMT(actual.filter + offset_after_allowlist, BPF_RET + BPF_K,
                 SECCOMP_RET_KILL);

  free(actual.filter);
}

TEST_F(FileTest, seccomp_global_allowlist_does_not_conflict) {
  std::string policy;
  for (const char *entry : get_libc_compatibility_allowlist()) {
    policy += entry;
    policy += ": 1\n";
  }

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  struct sock_fprog actual;
  int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_KILL,
        NO_LOGGING, false /* allow_dup_syscalls */,
        true /* include_libc_compatibility_allowlist */);
  fclose(policy_file);

  ASSERT_EQ(res, 0);
  std::span<const char *const> allowlist = get_libc_compatibility_allowlist();

  /* NOTE: due to how the global allowlist is added, this results in duplicate
   * allowlist entries in `actual` when both the global allowlist & the given
   * policy share a syscall. This is suboptimal, but the existence of global
   * allowlist entries is highly discouraged & the list should remain empty most
   * of the time. When it does contain entries, it should generally contain
   * fewer than five.
   */
  size_t offset_after_allowlist =
      ARCH_VALIDATION_LEN + 1 + 4 * allowlist.size();
  EXPECT_EQ(actual.len, offset_after_allowlist + 1);
  EXPECT_ARCH_VALIDATION(actual.filter);
  EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN, BPF_LD + BPF_W + BPF_ABS,
                 syscall_nr);
  for (size_t i = 0; i < allowlist.size() * 2; ++i) {
    int allowlist_entry_nr = lookup_syscall(allowlist[i % allowlist.size()],
                                            nullptr);
    EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 1 + 2 * i,
                         allowlist_entry_nr);
  }
  EXPECT_EQ_STMT(actual.filter + offset_after_allowlist, BPF_RET + BPF_K,
                 SECCOMP_RET_KILL);

  free(actual.filter);
}


TEST_F(FileTest, seccomp_mode1) {
  std::string policy =
      "read: 1\n"
      "write: 1\n"
      "rt_sigreturn: 1\n"
      "exit: 1\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);
  int res =
      test_compile_file("policy", policy_file, head_, &arg_blocks_, &labels_);
  fclose(policy_file);

  /*
   * Checks return value and that the blocks only allow expected syscalls.
   */
  ASSERT_EQ(res, 0);
  struct filter_block* curr_block = head_;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_read);
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_write);
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_rt_sigreturn);
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_exit);

  EXPECT_EQ(curr_block->next, nullptr);
}

TEST_F(FileTest, seccomp_read) {
  std::string policy =
      "read: arg0 == 0\n"
      "write: 1\n"
      "rt_sigreturn: 1\n"
      "exit: 1\n";

  const int LABEL_ID = 0;

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);
  int res =
      test_compile_file("policy", policy_file, head_, &arg_blocks_, &labels_);
  fclose(policy_file);

  /*
   * Checks return value, that the blocks only allow expected syscalls, and that
   * labels between |head_| and |arg_blocks_| match.
   */
  ASSERT_EQ(res, 0);
  struct filter_block* curr_block = head_;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW_SYSCALL_ARGS(curr_block->instrs, __NR_read, LABEL_ID, JUMP_JT,
                            JUMP_JF);
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_write);
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_rt_sigreturn);
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_exit);

  ASSERT_NE(arg_blocks_, nullptr);
  size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2;
  EXPECT_EQ(arg_blocks_->total_len, exp_total_len);

  /* First block is a label. */
  curr_block = arg_blocks_;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_EQ(curr_block->len, 1U);
  EXPECT_ACTUAL_LBL(curr_block->instrs, LABEL_ID);

  /* Second block is a comparison. */
  curr_block = curr_block->next;
  EXPECT_COMP(curr_block);

  /* Third block is a jump and a label (end of AND group). */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_GROUP_END(curr_block);

  /* Fourth block is SECCOMP_RET_KILL. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_KILL(curr_block);

  /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW(curr_block);

  EXPECT_EQ(curr_block->next, nullptr);
}

TEST_F(FileTest, multiline) {
  std::string policy =
      "read:\\\n1\n"
      "openat:arg0 \\\nin\\\n   \\\n5";

  const int LABEL_ID = 0;

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);
  int res =
      test_compile_file("policy", policy_file, head_, &arg_blocks_, &labels_);
  fclose(policy_file);

  /*
   * Policy should be valid.
   */
  ASSERT_EQ(res, 0);

  /* First block is the read. */
  struct filter_block* curr_block = head_;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW_SYSCALL(curr_block->instrs, __NR_read);

  /* Second block is the open. */
  curr_block = curr_block->next;
  ASSERT_NE(curr_block, nullptr);
  EXPECT_ALLOW_SYSCALL_ARGS(curr_block->instrs, __NR_openat, LABEL_ID, JUMP_JT,
                            JUMP_JF);

  EXPECT_EQ(curr_block->next, nullptr);
}

TEST(FilterTest, seccomp_mode1) {
  struct sock_fprog actual;
  std::string policy =
      "read: 1\n"
      "write: 1\n"
      "rt_sigreturn: 1\n"
      "exit: 1\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);

  /*
   * Checks return value, filter length, and that the filter
   * validates arch, loads syscall number, and
   * only allows expected syscalls.
   */
  ASSERT_EQ(res, 0);
  EXPECT_EQ(actual.len, 13);
  EXPECT_ARCH_VALIDATION(actual.filter);
  EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN, BPF_LD + BPF_W + BPF_ABS,
                 syscall_nr);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 1, __NR_read);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 3, __NR_write);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 5,
                       __NR_rt_sigreturn);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 7, __NR_exit);
  EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN + 9, BPF_RET + BPF_K,
                 SECCOMP_RET_KILL);

  free(actual.filter);
}

TEST(FilterTest, seccomp_mode1_with_check) {
  struct sock_fprog actual;
  std::string policy =
      "read: 1\n"
      "write: 1\n"
      "rt_sigreturn: 1\n"
      "exit: 1\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  int res = test_compile_filter(
      "policy", policy_file, &actual, ACTION_RET_KILL, NO_LOGGING, false
      /* allow duplicate syscalls */);
  fclose(policy_file);

  /*
   * Checks return value, filter length, and that the filter
   * validates arch, loads syscall number, and
   * only allows expected syscalls.
   */
  ASSERT_EQ(res, 0);
  EXPECT_EQ(actual.len, 13);
  EXPECT_ARCH_VALIDATION(actual.filter);
  EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN, BPF_LD + BPF_W + BPF_ABS,
                 syscall_nr);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 1, __NR_read);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 3, __NR_write);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 5,
                       __NR_rt_sigreturn);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 7, __NR_exit);
  EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN + 9, BPF_RET + BPF_K,
                 SECCOMP_RET_KILL);

  free(actual.filter);
}

/*
 * This fails even with allow_duplicate_syscalls set to true because the
 * creation of labels for the arguments causes conflicts which cause the
 * compile_filter function to fail.
 */
TEST(FilterTest, duplicate_read_with_args) {
  struct sock_fprog actual;
  std::string policy =
      "read: arg0 == 0\n"
      "read: arg1 == 1\n"
      "write: 1\n"
      "rt_sigreturn: 1\n"
      "exit: 1\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);
  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);

  ASSERT_EQ(res, -1);
}

/*
 * This does not fail because only one instance of read defines an argument.
 */
TEST(FilterTest, duplicate_read_with_one_arg) {
  struct sock_fprog actual;
  std::string policy =
      "read: arg0 == 0\n"
      "read: 1\n"
      "write: 1\n"
      "rt_sigreturn: 1\n"
      "exit: 1\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);
  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);

  /* TODO: Don't know how to generate a correct value to validate the filter
   * that is generated. */
  ASSERT_EQ(res, 0);
  free(actual.filter);
}

TEST(FilterTest, seccomp_mode1_trap) {
  struct sock_fprog actual;
  std::string policy =
      "read: 1\n"
      "write: 1\n"
      "rt_sigreturn: 1\n"
      "exit: 1\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  int res =
      test_compile_filter("policy", policy_file, &actual, ACTION_RET_TRAP);
  fclose(policy_file);

  /*
   * Checks return value, filter length, and that the filter
   * validates arch, loads syscall number, and
   * only allows expected syscalls.
   */
  ASSERT_EQ(res, 0);
  EXPECT_EQ(actual.len, 13);
  EXPECT_ARCH_VALIDATION(actual.filter);
  EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN, BPF_LD + BPF_W + BPF_ABS,
                 syscall_nr);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 1, __NR_read);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 3, __NR_write);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 5,
                       __NR_rt_sigreturn);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 7, __NR_exit);
  EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN + 9, BPF_RET + BPF_K,
                 SECCOMP_RET_TRAP);

  free(actual.filter);
}

TEST(FilterTest, seccomp_mode1_log) {
  struct sock_fprog actual;
  std::string policy =
      "read: 1\n"
      "write: 1\n"
      "rt_sigreturn: 1\n"
      "exit: 1\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_LOG,
                                USE_RET_LOG_LOGGING);
  fclose(policy_file);

  /*
   * Checks return value, filter length, and that the filter
   * validates arch, loads syscall number, and
   * only allows expected syscalls.
   */
  ASSERT_EQ(res, 0);
  EXPECT_EQ(actual.len, 13);
  EXPECT_ARCH_VALIDATION(actual.filter);
  EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN, BPF_LD + BPF_W + BPF_ABS,
                 syscall_nr);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 1, __NR_read);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 3, __NR_write);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 5,
                       __NR_rt_sigreturn);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 7, __NR_exit);
  EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN + 9, BPF_RET + BPF_K,
                 SECCOMP_RET_LOG);

  free(actual.filter);
}

TEST(FilterTest, seccomp_mode1_log_fails) {
  struct sock_fprog actual;
  std::string policy =
      "read: 1\n"
      "write: 1\n"
      "rt_sigreturn: 1\n"
      "exit: 1\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_LOG,
                                NO_LOGGING);
  fclose(policy_file);

  /*
   * ACTION_RET_LOG should never be used without allowing logging.
   */
  ASSERT_EQ(res, -1);
}

TEST(FilterTest, seccomp_mode1_ret_kill_process) {
  struct sock_fprog actual;
  std::string policy =
      "read: 1\n"
      "write: 1\n"
      "rt_sigreturn: 1\n"
      "exit: 1\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  int res = test_compile_filter("policy", policy_file, &actual,
                                ACTION_RET_KILL_PROCESS, NO_LOGGING);
  fclose(policy_file);

  /*
   * Checks return value, filter length, and that the filter
   * validates arch, loads syscall number, and
   * only allows expected syscalls.
   */
  ASSERT_EQ(res, 0);
  EXPECT_EQ(actual.len, 13);
  EXPECT_ARCH_VALIDATION(actual.filter);
  EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN, BPF_LD + BPF_W + BPF_ABS,
                 syscall_nr);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 1, __NR_read);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 3, __NR_write);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 5,
                       __NR_rt_sigreturn);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 7, __NR_exit);
  EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN + 9, BPF_RET + BPF_K,
                 SECCOMP_RET_KILL_PROCESS);

  free(actual.filter);
}

TEST(FilterTest, seccomp_read_write) {
  struct sock_fprog actual;
  std::string policy =
      "read: arg0 == 0\n"
      "write: arg0 == 1 || arg0 == 2\n"
      "rt_sigreturn: 1\n"
      "exit: 1\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);

  /*
   * Checks return value, filter length, and that the filter
   * validates arch, loads syscall number, and
   * only allows expected syscalls, jumping to correct arg filter
   * offsets.
   */
  ASSERT_EQ(res, 0);
  size_t exp_total_len = 27 + 3 * (BPF_ARG_COMP_LEN + 1);
  EXPECT_EQ(actual.len, exp_total_len);

  EXPECT_ARCH_VALIDATION(actual.filter);
  EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN, BPF_LD + BPF_W + BPF_ABS,
                 syscall_nr);
  EXPECT_ALLOW_SYSCALL_ARGS(actual.filter + ARCH_VALIDATION_LEN + 1, __NR_read,
                            7, 0, 0);
  EXPECT_ALLOW_SYSCALL_ARGS(actual.filter + ARCH_VALIDATION_LEN + 3, __NR_write,
                            12 + BPF_ARG_COMP_LEN, 0, 0);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 5,
                       __NR_rt_sigreturn);
  EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 7, __NR_exit);
  EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN + 9, BPF_RET + BPF_K,
                 SECCOMP_RET_KILL);

  free(actual.filter);
}

TEST(FilterTest, misplaced_whitespace) {
  struct sock_fprog actual;
  std::string policy = "read :1\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);

  /* Checks return value and filter length. */
  ASSERT_EQ(res, 0);
  EXPECT_EQ(actual.len, ARCH_VALIDATION_LEN + 1 /* load syscall nr */ +
                            ALLOW_SYSCALL_LEN + 1 /* ret kill */);
  free(actual.filter);
}

TEST(FilterTest, missing_atom) {
  struct sock_fprog actual;
  std::string policy = "open:\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);
  ASSERT_NE(res, 0);
}

TEST(FilterTest, whitespace_atom) {
  struct sock_fprog actual;
  std::string policy = "open:\t    \n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);
  ASSERT_NE(res, 0);
}

TEST(FilterTest, invalid_name) {
  struct sock_fprog actual;
  std::string policy = "notasyscall: 1\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);
  ASSERT_NE(res, 0);
}

TEST(FilterTest, invalid_arg) {
  struct sock_fprog actual;
  std::string policy = "open: argnn ==\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);
  ASSERT_NE(res, 0);
}

TEST(FilterTest, invalid_tokens) {
  struct sock_fprog actual;
  std::string policy = "read: arg0 == 1 |||| arg0 == 2\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);
  ASSERT_NE(res, 0);
}

TEST(FilterTest, nonexistent) {
  struct sock_fprog actual;
  int res = test_compile_filter("policy", nullptr, &actual);
  ASSERT_NE(res, 0);
}

TEST(FilterTest, log) {
  struct sock_fprog actual;
  std::string policy =
      "read: 1\n"
      "write: 1\n"
      "rt_sigreturn: 1\n"
      "exit: 1\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_TRAP,
                                USE_SIGSYS_LOGGING);
  fclose(policy_file);

  size_t i;
  size_t index = 0;
  /*
   * Checks return value, filter length, and that the filter
   * validates arch, loads syscall number, only allows expected syscalls,
   * and returns TRAP on failure.
   * NOTE(jorgelo): the filter is longer since we add the syscalls needed
   * for logging.
   */
  ASSERT_EQ(res, 0);
  EXPECT_EQ(actual.len, 13 + 2 * log_syscalls_len);
  EXPECT_ARCH_VALIDATION(actual.filter);
  EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN, BPF_LD + BPF_W + BPF_ABS,
                 syscall_nr);

  index = ARCH_VALIDATION_LEN + 1;
  for (i = 0; i < log_syscalls_len; i++)
    EXPECT_ALLOW_SYSCALL(actual.filter + (index + 2 * i),
                         lookup_syscall(log_syscalls[i], NULL));

  index += 2 * log_syscalls_len;

  EXPECT_ALLOW_SYSCALL(actual.filter + index, __NR_read);
  EXPECT_ALLOW_SYSCALL(actual.filter + index + 2, __NR_write);
  EXPECT_ALLOW_SYSCALL(actual.filter + index + 4, __NR_rt_sigreturn);
  EXPECT_ALLOW_SYSCALL(actual.filter + index + 6, __NR_exit);
  EXPECT_EQ_STMT(actual.filter + index + 8, BPF_RET + BPF_K, SECCOMP_RET_TRAP);

  free(actual.filter);
}

TEST(FilterTest, allow_log_but_kill) {
  struct sock_fprog actual;
  std::string policy =
      "read: 1\n"
      "write: 1\n"
      "rt_sigreturn: 1\n"
      "exit: 1\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_KILL,
                                USE_SIGSYS_LOGGING);
  fclose(policy_file);

  size_t i;
  size_t index = 0;
  /*
   * Checks return value, filter length, and that the filter
   * validates arch, loads syscall number, only allows expected syscalls,
   * and kills on failure.
   * NOTE(jorgelo): the filter is longer since we add the syscalls needed
   * for logging.
   */
  ASSERT_EQ(res, 0);
  EXPECT_EQ(actual.len, 13 + 2 * log_syscalls_len);
  EXPECT_ARCH_VALIDATION(actual.filter);
  EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN, BPF_LD + BPF_W + BPF_ABS,
                 syscall_nr);

  index = ARCH_VALIDATION_LEN + 1;
  for (i = 0; i < log_syscalls_len; i++)
    EXPECT_ALLOW_SYSCALL(actual.filter + (index + 2 * i),
                         lookup_syscall(log_syscalls[i], NULL));

  index += 2 * log_syscalls_len;

  EXPECT_ALLOW_SYSCALL(actual.filter + index, __NR_read);
  EXPECT_ALLOW_SYSCALL(actual.filter + index + 2, __NR_write);
  EXPECT_ALLOW_SYSCALL(actual.filter + index + 4, __NR_rt_sigreturn);
  EXPECT_ALLOW_SYSCALL(actual.filter + index + 6, __NR_exit);
  EXPECT_EQ_STMT(actual.filter + index + 8, BPF_RET + BPF_K, SECCOMP_RET_KILL);

  free(actual.filter);
}

TEST(FilterTest, frequency) {
  struct sock_fprog actual;
  std::string frequency = "@frequency ./path/is/ignored.frequency\n";

  FILE* policy_file = write_to_pipe(frequency);
  ASSERT_NE(policy_file, nullptr);
  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);
  EXPECT_EQ(res, 0);

  free(actual.filter);
}

TEST(FilterTest, include_invalid_token) {
  struct sock_fprog actual;
  std::string invalid_token = "@unclude ./test/seccomp.policy\n";

  FILE* policy_file = write_to_pipe(invalid_token);
  ASSERT_NE(policy_file, nullptr);
  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);
  EXPECT_NE(res, 0);
}

TEST(FilterTest, include_no_space) {
  struct sock_fprog actual;
  std::string no_space = "@includetest/seccomp.policy\n";

  FILE* policy_file = write_to_pipe(no_space);
  ASSERT_NE(policy_file, nullptr);
  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);
  EXPECT_NE(res, 0);
}

TEST(FilterTest, include_double_token) {
  struct sock_fprog actual;
  std::string double_token = "@includeinclude ./test/seccomp.policy\n";

  FILE* policy_file = write_to_pipe(double_token);
  ASSERT_NE(policy_file, nullptr);
  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);
  EXPECT_NE(res, 0);
}

TEST(FilterTest, include_no_file) {
  struct sock_fprog actual;
  std::string no_file = "@include\n";

  FILE* policy_file = write_to_pipe(no_file);
  ASSERT_NE(policy_file, nullptr);
  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);
  EXPECT_NE(res, 0);
}

TEST(FilterTest, include_space_no_file) {
  struct sock_fprog actual;
  std::string space_no_file = "@include \n";

  FILE* policy_file = write_to_pipe(space_no_file);
  ASSERT_NE(policy_file, nullptr);
  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);
  EXPECT_NE(res, 0);
}

TEST(FilterTest, include_implicit_relative_path) {
  struct sock_fprog actual;
  std::string implicit_relative_path = "@include test/seccomp.policy\n";

  FILE* policy_file = write_to_pipe(implicit_relative_path);
  ASSERT_NE(policy_file, nullptr);
  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);
  EXPECT_NE(res, 0);
}

TEST(FilterTest, include_extra_text) {
  struct sock_fprog actual;
  std::string extra_text = "@include /some/file: sneaky comment\n";

  FILE* policy_file = write_to_pipe(extra_text);
  ASSERT_NE(policy_file, nullptr);
  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);
  EXPECT_NE(res, 0);
}

TEST(FilterTest, include_split_filename) {
  struct sock_fprog actual;
  std::string split_filename = "@include /some/file:colon.policy\n";

  FILE* policy_file = write_to_pipe(split_filename);
  ASSERT_NE(policy_file, nullptr);
  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);
  EXPECT_NE(res, 0);
}

TEST(FilterTest, include_nonexistent_file) {
  struct sock_fprog actual;
  std::string include_policy = "@include ./nonexistent.policy\n";

  FILE* policy_file = write_to_pipe(include_policy);
  ASSERT_NE(policy_file, nullptr);

  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);

  ASSERT_NE(res, 0);
}

// TODO(jorgelo): Android unit tests don't currently support data files.
// Re-enable by creating a temporary policy file at runtime.
#if !defined(__ANDROID__)

TEST(FilterTest, include) {
  struct sock_fprog compiled_plain;
  struct sock_fprog compiled_with_include;

  std::string policy_plain =
      "read: 1\n"
      "write: 1\n"
      "rt_sigreturn: 1\n"
      "exit: 1\n";

  FILE* file_plain = write_to_pipe(policy_plain);
  ASSERT_NE(file_plain, nullptr);
  int res_plain = test_compile_filter("policy", file_plain, &compiled_plain,
                                      ACTION_RET_KILL);
  fclose(file_plain);

  std::string policy_with_include =
      "@include " + source_path("test/seccomp.policy") + "\n";

  FILE* file_with_include = write_to_pipe(policy_with_include);
  ASSERT_NE(file_with_include, nullptr);
  int res_with_include = test_compile_filter(
      "policy", file_with_include, &compiled_with_include, ACTION_RET_KILL);
  fclose(file_with_include);

  /*
   * Checks that filter length is the same for a plain policy and an equivalent
   * policy with an @include statement. Also checks that the filter generated
   * from the policy with an @include statement is exactly the same as one
   * generated from a plain policy.
   */
  ASSERT_EQ(res_plain, 0);
  ASSERT_EQ(res_with_include, 0);

  EXPECT_EQ(compiled_plain.len, 13);
  EXPECT_EQ(compiled_with_include.len, 13);

  EXPECT_ARCH_VALIDATION(compiled_with_include.filter);
  EXPECT_EQ_STMT(compiled_with_include.filter + ARCH_VALIDATION_LEN,
                 BPF_LD + BPF_W + BPF_ABS, syscall_nr);
  EXPECT_ALLOW_SYSCALL(compiled_with_include.filter + ARCH_VALIDATION_LEN + 1,
                       __NR_read);
  EXPECT_ALLOW_SYSCALL(compiled_with_include.filter + ARCH_VALIDATION_LEN + 3,
                       __NR_write);
  EXPECT_ALLOW_SYSCALL(compiled_with_include.filter + ARCH_VALIDATION_LEN + 5,
                       __NR_rt_sigreturn);
  EXPECT_ALLOW_SYSCALL(compiled_with_include.filter + ARCH_VALIDATION_LEN + 7,
                       __NR_exit);
  EXPECT_EQ_STMT(compiled_with_include.filter + ARCH_VALIDATION_LEN + 9,
                 BPF_RET + BPF_K, SECCOMP_RET_KILL);

  free(compiled_plain.filter);
  free(compiled_with_include.filter);
}

TEST(FilterTest, include_same_syscalls) {
  struct sock_fprog actual;
  std::string policy =
      "read: 1\n"
      "write: 1\n"
      "rt_sigreturn: 1\n"
      "exit: 1\n"
      "@include " +
      source_path("test/seccomp.policy") + "\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);

  ASSERT_EQ(res, 0);
  EXPECT_EQ(actual.len, ARCH_VALIDATION_LEN + 1 /* load syscall nr */ +
                            2 * 8 /* check syscalls twice */ +
                            1 /* filter return */);
  free(actual.filter);
}

TEST(FilterTest, include_same_syscalls_with_check) {
  struct sock_fprog actual;
  std::string policy =
      "read: 1\n"
      "write: 1\n"
      "rt_sigreturn: 1\n"
      "exit: 1\n"
      "@include " +
      source_path("test/seccomp.policy") + "\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  int res = test_compile_filter(
      "policy", policy_file, &actual, ACTION_RET_KILL, NO_LOGGING, false
      /* allow duplicate syscalls */);
  fclose(policy_file);

  ASSERT_EQ(res, -1);
}

TEST(FilterTest, include_two) {
  struct sock_fprog actual;
  std::string policy = "@include " + source_path("test/seccomp.policy") + "\n" +
                       "@include " + source_path("test/seccomp.policy") + "\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);

  ASSERT_EQ(res, 0);
  EXPECT_EQ(actual.len, ARCH_VALIDATION_LEN + 1 /* load syscall nr */ +
                            2 * 8 /* check syscalls twice */ +
                            1 /* filter return */);
  free(actual.filter);
}

TEST(FilterTest, include_invalid_policy) {
  struct sock_fprog actual;
  std::string policy =
      "read: 1\n"
      "write: 1\n"
      "rt_sigreturn: 1\n"
      "exit: 1\n"
      "@include ./test/invalid_syscall_name.policy\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  /* Ensure the included (invalid) policy file exists. */
  FILE* included_file =
      fopen(source_path("test/invalid_syscall_name.policy").c_str(), "re");
  ASSERT_NE(included_file, nullptr);
  fclose(included_file);

  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);

  ASSERT_NE(res, 0);
}

TEST(FilterTest, include_nested) {
  struct sock_fprog actual;
  std::string policy = "@include ./test/nested.policy\n";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);

  /* Ensure the policy file exists. */
  FILE* included_file = fopen(source_path("test/nested.policy").c_str(), "re");
  ASSERT_NE(included_file, nullptr);
  fclose(included_file);

  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);

  ASSERT_NE(res, 0);
}

#endif  // !__ANDROID__

TEST(FilterTest, error_cleanup_leak) {
  struct sock_fprog actual;
  std::string policy =
      "read:&&\n"
      "read:&&";

  FILE* policy_file = write_to_pipe(policy);
  ASSERT_NE(policy_file, nullptr);
  int res = test_compile_filter("policy", policy_file, &actual);
  fclose(policy_file);

  /*
   * Policy is malformed, but process should not leak.
   */
  ASSERT_EQ(res, -1);
}
