diff --git a/src/asm_files.cpp b/src/asm_files.cpp index b74a3fd38..e513d9c0e 100644 --- a/src/asm_files.cpp +++ b/src/asm_files.cpp @@ -88,7 +88,7 @@ static std::tuple get_value(const ELFIO::const // parse_maps_sections processes all maps sections in the provided ELF file by calling the platform-specific maps' // parser. The section index of each maps section is inserted into section_indices. -static size_t parse_map_sections(const ebpf_verifier_options_t* options, const ebpf_platform_t* platform, +static size_t parse_map_sections(const ebpf_verifier_options_t& options, const ebpf_platform_t* platform, const ELFIO::elfio& reader, vector& map_descriptors, std::set& section_indices, const ELFIO::const_symbol_section_accessor& symbols) { @@ -116,8 +116,7 @@ static size_t parse_map_sections(const ebpf_verifier_options_t* options, const e if (s->get_size() % map_record_size != 0) { throw std::runtime_error("bad maps section size"); } - platform->parse_maps_section(map_descriptors, s->get_data(), map_record_size, map_count, platform, - *options); + platform->parse_maps_section(map_descriptors, s->get_data(), map_record_size, map_count, platform, options); } section_indices.insert(s->get_index()); } @@ -125,7 +124,7 @@ static size_t parse_map_sections(const ebpf_verifier_options_t* options, const e return map_record_size; } -vector read_elf(const string& path, const string& desired_section, const ebpf_verifier_options_t* options, +vector read_elf(const string& path, const string& desired_section, const ebpf_verifier_options_t& options, const ebpf_platform_t* platform) { if (std::ifstream stream{path, std::ios::in | std::ios::binary}) { return read_elf(stream, path, desired_section, options, platform); @@ -280,10 +279,7 @@ std::map parse_map_section(const libbtf::btf_type_data& btf } vector read_elf(std::istream& input_stream, const std::string& path, const std::string& desired_section, - const ebpf_verifier_options_t* options, const ebpf_platform_t* platform) { - if (options == nullptr) { - options = &ebpf_verifier_default_options; - } + const ebpf_verifier_options_t& options, const ebpf_platform_t* platform) { ELFIO::elfio reader; if (!reader.load(input_stream)) { throw std::runtime_error("Can't process ELF file " + path); @@ -311,7 +307,7 @@ vector read_elf(std::istream& input_stream, const std::string& path if (btf) { // Parse the BTF type data. btf_data = vector_of(*btf); - if (options->dump_btf_types_json) { + if (options.dump_btf_types_json) { std::stringstream output; std::cout << "Dumping BTF data for" << path << std::endl; // Dump the BTF data to cout for debugging purposes. diff --git a/src/asm_files.hpp b/src/asm_files.hpp index c22a5cb2d..f8534d7ef 100644 --- a/src/asm_files.hpp +++ b/src/asm_files.hpp @@ -11,9 +11,9 @@ std::vector read_raw(std::string path, program_info info); std::vector read_elf(const std::string& path, const std::string& desired_section, - const ebpf_verifier_options_t* options, const ebpf_platform_t* platform); + const ebpf_verifier_options_t& options, const ebpf_platform_t* platform); std::vector read_elf(std::istream& input_stream, const std::string& path, - const std::string& desired_section, const ebpf_verifier_options_t* options, + const std::string& desired_section, const ebpf_verifier_options_t& options, const ebpf_platform_t* platform); void write_binary_file(std::string path, const char* data, size_t size); diff --git a/src/config.cpp b/src/config.cpp deleted file mode 100644 index 6c40e86d2..000000000 --- a/src/config.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Prevail Verifier contributors. -// SPDX-License-Identifier: MIT -#include "config.hpp" - -const ebpf_verifier_options_t ebpf_verifier_default_options = { - .check_termination = false, - .assume_assertions = false, - .print_invariants = false, - .print_failures = false, - .simplify = true, - .mock_map_fds = true, - .strict = false, - .print_line_info = false, - .allow_division_by_zero = true, - .setup_constraints = true, - .big_endian = false, -}; diff --git a/src/config.hpp b/src/config.hpp index 26963b0a9..c33ff5f37 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -2,25 +2,26 @@ // SPDX-License-Identifier: MIT #pragma once +#include "crab/cfg.hpp" + struct ebpf_verifier_options_t { - bool check_termination; - bool assume_assertions; - bool print_invariants; - bool print_failures; - bool simplify; + prepare_cfg_options cfg_opts; + bool assume_assertions = false; + bool print_invariants = false; + bool print_failures = false; // False to use actual map fd's, true to use mock fd's. - bool mock_map_fds; + bool mock_map_fds = true; // True to do additional checks for some things that would fail at runtime. - bool strict; + bool strict = false; - bool print_line_info; - bool allow_division_by_zero; - bool setup_constraints; - bool big_endian; + bool print_line_info = false; + bool allow_division_by_zero = true; + bool setup_constraints = true; + bool big_endian = false; - bool dump_btf_types_json; + bool dump_btf_types_json = false; }; struct ebpf_verifier_stats_t { @@ -29,5 +30,4 @@ struct ebpf_verifier_stats_t { int max_loop_count; }; -extern const ebpf_verifier_options_t ebpf_verifier_default_options; extern thread_local ebpf_verifier_options_t thread_local_options; diff --git a/src/crab/cfg.hpp b/src/crab/cfg.hpp index 59435ebcf..3a30b4a89 100644 --- a/src/crab/cfg.hpp +++ b/src/crab/cfg.hpp @@ -623,7 +623,7 @@ struct prepare_cfg_options { /// When true, simplifies the control flow graph by merging basic blocks. bool simplify = true; /// When true, verifies that the program terminates. - bool check_for_termination = true; + bool check_for_termination = false; /// When true, ensures the program has a valid exit block. bool must_have_exit = true; }; diff --git a/src/crab/ebpf_domain.cpp b/src/crab/ebpf_domain.cpp index 7cbcee9dc..c638a6ba5 100644 --- a/src/crab/ebpf_domain.cpp +++ b/src/crab/ebpf_domain.cpp @@ -825,7 +825,7 @@ ebpf_domain_t ebpf_domain_t::calculate_constant_limits() { inv += r.shared_offset >= 0; inv += r.packet_offset <= variable_t::packet_size(); inv += r.packet_offset >= 0; - if (thread_local_options.check_termination) { + if (thread_local_options.cfg_opts.check_for_termination) { for (const variable_t counter : variable_t::get_loop_counters()) { inv += counter <= std::numeric_limits::max(); inv += counter >= 0; diff --git a/src/crab/fwd_analyzer.cpp b/src/crab/fwd_analyzer.cpp index 04c464524..14bde9ea6 100644 --- a/src/crab/fwd_analyzer.cpp +++ b/src/crab/fwd_analyzer.cpp @@ -116,13 +116,14 @@ class interleaved_fwd_fixpoint_iterator_t final { void operator()(const std::shared_ptr& cycle); - friend std::pair run_forward_analyzer(const cfg_t& cfg, ebpf_domain_t entry_inv); + friend std::pair run_forward_analyzer(const cfg_t& cfg, + ebpf_domain_t entry_inv); }; std::pair run_forward_analyzer(const cfg_t& cfg, ebpf_domain_t entry_inv) { // Go over the CFG in weak topological order (accounting for loops). interleaved_fwd_fixpoint_iterator_t analyzer(cfg); - if (thread_local_options.check_termination) { + if (thread_local_options.cfg_opts.check_for_termination) { // Initialize loop counters for potential loop headers. // This enables enforcement of upper bounds on loop iterations // during program verification. diff --git a/src/crab_verifier.cpp b/src/crab_verifier.cpp index a0bd7be91..11503f7b0 100644 --- a/src/crab_verifier.cpp +++ b/src/crab_verifier.cpp @@ -94,7 +94,7 @@ static checks_db generate_report(const cfg_t& cfg, const crab::invariant_table_t } } - if (thread_local_options.check_termination) { + if (thread_local_options.cfg_opts.check_for_termination) { // Gather the upper bound of loop counts from post-invariants. for (const auto [label, inv] : post_invariants) { if (inv.is_bottom()) { @@ -162,12 +162,13 @@ static checks_db get_analysis_report(std::ostream& s, const cfg_t& cfg, const cr return db; } -static checks_db get_ebpf_report(std::ostream& s, const cfg_t& cfg, program_info info, const ebpf_verifier_options_t* options, +static checks_db get_ebpf_report(std::ostream& s, const cfg_t& cfg, program_info info, + const ebpf_verifier_options_t& options, const std::optional& prog = std::nullopt) { global_program_info = std::move(info); crab::domains::clear_global_state(); crab::variable_t::clear_thread_local_state(); - thread_local_options = *options; + thread_local_options = options; try { // Get dictionaries of pre-invariants and post-invariants for each basic block. @@ -183,11 +184,8 @@ static checks_db get_ebpf_report(std::ostream& s, const cfg_t& cfg, program_info } /// Returned value is true if the program passes verification. -bool run_ebpf_analysis(std::ostream& s, const cfg_t& cfg, const program_info& info, const ebpf_verifier_options_t* options, - ebpf_verifier_stats_t* stats) { - if (options == nullptr) { - options = &ebpf_verifier_default_options; - } +bool run_ebpf_analysis(std::ostream& s, const cfg_t& cfg, const program_info& info, + const ebpf_verifier_options_t& options, ebpf_verifier_stats_t* stats) { const checks_db report = get_ebpf_report(s, cfg, info, options); if (stats) { stats->total_unreachable = report.total_unreachable; @@ -220,10 +218,7 @@ std::tuple ebpf_analyze_program_for_test(std::ostream& o throw std::runtime_error("Entry invariant is inconsistent"); } try { - const cfg_t cfg = prepare_cfg(prog, info, - {.simplify = options.simplify, - .check_for_termination = options.check_termination, - .must_have_exit = false}); + const cfg_t cfg = prepare_cfg(prog, info, options.cfg_opts); auto [pre_invariants, post_invariants] = run_forward_analyzer(cfg, std::move(entry_inv)); const checks_db report = get_analysis_report(std::cerr, cfg, pre_invariants, post_invariants); print_report(os, report, prog, false); @@ -239,24 +234,19 @@ std::tuple ebpf_analyze_program_for_test(std::ostream& o /// Returned value is true if the program passes verification. bool ebpf_verify_program(std::ostream& os, const InstructionSeq& prog, const program_info& info, - const ebpf_verifier_options_t* options, ebpf_verifier_stats_t* stats) { - if (options == nullptr) { - options = &ebpf_verifier_default_options; - } - + const ebpf_verifier_options_t& options, ebpf_verifier_stats_t* stats) { // Convert the instruction sequence to a control-flow graph // in a "passive", non-deterministic form. - const cfg_t cfg = - prepare_cfg(prog, info, {.simplify = options->simplify, .check_for_termination = options->check_termination}); + const cfg_t cfg = prepare_cfg(prog, info, options.cfg_opts); std::optional prog_opt = std::nullopt; - if (options->print_failures) { + if (options.print_failures) { prog_opt = prog; } const checks_db report = get_ebpf_report(os, cfg, info, options, prog_opt); - if (options->print_failures) { - print_report(os, report, prog, options->print_line_info); + if (options.print_failures) { + print_report(os, report, prog, options.print_line_info); } if (stats) { stats->total_unreachable = report.total_unreachable; diff --git a/src/crab_verifier.hpp b/src/crab_verifier.hpp index 6694123c8..1da1d5e22 100644 --- a/src/crab_verifier.hpp +++ b/src/crab_verifier.hpp @@ -7,11 +7,11 @@ #include "spec_type_descriptors.hpp" #include "string_constraints.hpp" -bool run_ebpf_analysis(std::ostream& s, const cfg_t& cfg, const program_info& info, const ebpf_verifier_options_t* options, - ebpf_verifier_stats_t* stats); +bool run_ebpf_analysis(std::ostream& s, const cfg_t& cfg, const program_info& info, + const ebpf_verifier_options_t& options, ebpf_verifier_stats_t* stats); bool ebpf_verify_program(std::ostream& os, const InstructionSeq& prog, const program_info& info, - const ebpf_verifier_options_t* options, ebpf_verifier_stats_t* stats); + const ebpf_verifier_options_t& options, ebpf_verifier_stats_t* stats); using string_invariant_map = std::map; diff --git a/src/main/check.cpp b/src/main/check.cpp index aad23f432..da32ce2cd 100644 --- a/src/main/check.cpp +++ b/src/main/check.cpp @@ -70,7 +70,7 @@ int main(int argc, char** argv) { // Always call ebpf_verifier_clear_thread_local_state on scope exit. at_scope_exit clear_thread_local_state; - ebpf_verifier_options_t ebpf_verifier_options = ebpf_verifier_default_options; + ebpf_verifier_options_t ebpf_verifier_options; crab::CrabEnableWarningMsg(false); @@ -97,7 +97,7 @@ int main(int argc, char** argv) { ->capture_default_str() ->check(CLI::IsMember({"stats", "linux", "zoneCrab", "cfg"})); - app.add_flag("--termination,!--no-verify-termination", ebpf_verifier_options.check_termination, + app.add_flag("--termination,!--no-verify-termination", ebpf_verifier_options.cfg_opts.check_for_termination, "Verify termination. Default: ignore") ->group("Features"); @@ -124,7 +124,7 @@ int main(int argc, char** argv) { ->expected(0, _conformance_groups.size()) ->check(CLI::IsMember(_get_conformance_group_names())); - app.add_flag("--simplify,!--no-simplify", ebpf_verifier_options.simplify, + app.add_flag("--simplify,!--no-simplify", ebpf_verifier_options.cfg_opts.simplify, "Simplify the CFG before analysis by merging chains of instructions into a single basic block. " "Default: enabled") ->group("Verbosity"); @@ -193,7 +193,7 @@ int main(int argc, char** argv) { // Read a set of raw program sections from an ELF file. vector raw_progs; try { - raw_progs = read_elf(filename, desired_section, &ebpf_verifier_options, &platform); + raw_progs = read_elf(filename, desired_section, ebpf_verifier_options, &platform); } catch (std::runtime_error& e) { std::cerr << "error: " << e.what() << std::endl; return 1; @@ -208,7 +208,7 @@ int main(int argc, char** argv) { if (!desired_section.empty() && raw_progs.empty()) { // We could not find the desired program, so get the full list // of possibilities. - raw_progs = read_elf(filename, string(), &ebpf_verifier_options, &platform); + raw_progs = read_elf(filename, string(), ebpf_verifier_options, &platform); } for (const raw_program& raw_prog : raw_progs) { std::cout << "section=" << raw_prog.section_name << " function=" << raw_prog.function_name << std::endl; @@ -232,15 +232,12 @@ int main(int argc, char** argv) { print_map_descriptors(global_program_info->map_descriptors, out); } - prepare_cfg_options cfg_options = {.simplify = ebpf_verifier_options.simplify, - .check_for_termination = ebpf_verifier_options.check_termination}; - if (domain == "zoneCrab") { ebpf_verifier_stats_t verifier_stats; const auto [res, seconds] = timed_execution([&] { - return ebpf_verify_program(std::cout, prog, raw_prog.info, &ebpf_verifier_options, &verifier_stats); + return ebpf_verify_program(std::cout, prog, raw_prog.info, ebpf_verifier_options, &verifier_stats); }); - if (res && ebpf_verifier_options.check_termination && + if (res && ebpf_verifier_options.cfg_opts.check_for_termination && (ebpf_verifier_options.print_failures || ebpf_verifier_options.print_invariants)) { std::cout << "Program terminates within " << verifier_stats.max_loop_count << " loop iterations\n"; } @@ -253,7 +250,7 @@ int main(int argc, char** argv) { return !res; } else if (domain == "stats") { // Convert the instruction sequence to a control-flow graph. - const cfg_t cfg = prepare_cfg(prog, raw_prog.info, cfg_options); + const cfg_t cfg = prepare_cfg(prog, raw_prog.info, ebpf_verifier_options.cfg_opts); // Just print eBPF program stats. auto stats = collect_stats(cfg); @@ -267,7 +264,7 @@ int main(int argc, char** argv) { std::cout << "\n"; } else if (domain == "cfg") { // Convert the instruction sequence to a control-flow graph. - const cfg_t cfg = prepare_cfg(prog, raw_prog.info, cfg_options); + const cfg_t cfg = prepare_cfg(prog, raw_prog.info, ebpf_verifier_options.cfg_opts); std::cout << cfg; std::cout << "\n"; } else { diff --git a/src/test/ebpf_yaml.cpp b/src/test/ebpf_yaml.cpp index 5c9194d67..7928cd93a 100644 --- a/src/test/ebpf_yaml.cpp +++ b/src/test/ebpf_yaml.cpp @@ -168,10 +168,10 @@ static InstructionSeq raw_cfg_to_instruction_seq(const vector& raw_options) { - ebpf_verifier_options_t options = ebpf_verifier_default_options; + ebpf_verifier_options_t options; // Use ~simplify for YAML tests unless otherwise specified. - options.simplify = false; + options.cfg_opts.simplify = false; // All YAML tests use !setup_constraints. options.setup_constraints = false; @@ -182,15 +182,18 @@ static ebpf_verifier_options_t raw_options_to_options(const std::set& ra // Default to not assuming assertions. options.assume_assertions = false; + // Permit test cases to not have an exit instruction. + options.cfg_opts.must_have_exit = false; + for (const string& name : raw_options) { if (name == "!allow_division_by_zero") { options.allow_division_by_zero = false; } else if (name == "termination") { - options.check_termination = true; + options.cfg_opts.check_for_termination = true; } else if (name == "strict") { options.strict = true; } else if (name == "simplify") { - options.simplify = true; + options.cfg_opts.simplify = true; } else if (name == "big_endian") { options.big_endian = true; } else if (name == "!big_endian") { @@ -248,7 +251,7 @@ std::optional run_yaml_test_case(TestCase test_case, bool debug) { if (debug) { test_case.options.print_failures = true; test_case.options.print_invariants = true; - test_case.options.simplify = false; + test_case.options.cfg_opts.simplify = false; } ebpf_context_descriptor_t context_descriptor{64, 0, 4, -1}; @@ -352,12 +355,12 @@ ConformanceTestResult run_conformance_test_case(const std::vector& me auto& prog = std::get(prog_or_error); - ebpf_verifier_options_t options = ebpf_verifier_default_options; + ebpf_verifier_options_t options; if (debug) { print(prog, std::cout, {}); options.print_failures = true; options.print_invariants = true; - options.simplify = false; + options.cfg_opts.simplify = false; } try { diff --git a/src/test/test_print.cpp b/src/test/test_print.cpp index b7e8eaf94..bcf1675f1 100644 --- a/src/test/test_print.cpp +++ b/src/test/test_print.cpp @@ -22,8 +22,7 @@ void verify_printed_string(const std::string& file) { std::stringstream generated_output; - auto raw_progs = - read_elf(std::string(TEST_OBJECT_FILE_DIRECTORY) + file + ".o", "", nullptr, &g_ebpf_platform_linux); + auto raw_progs = read_elf(std::string(TEST_OBJECT_FILE_DIRECTORY) + file + ".o", "", {}, &g_ebpf_platform_linux); const raw_program& raw_prog = raw_progs.back(); std::variant prog_or_error = unmarshal(raw_prog); auto program = std::get_if(&prog_or_error); diff --git a/src/test/test_verify.cpp b/src/test/test_verify.cpp index 8835f440a..dbeb0c5d0 100644 --- a/src/test/test_verify.cpp +++ b/src/test/test_verify.cpp @@ -4,13 +4,13 @@ #include #include -#define FAIL_LOAD_ELF(dirname, filename, sectionname) \ - TEST_CASE("Try loading nonexisting program: " dirname "/" filename, "[elf]") { \ - try { \ - read_elf("ebpf-samples/" dirname "/" filename, sectionname, nullptr, &g_ebpf_platform_linux); \ - REQUIRE(false); \ - } catch (const std::runtime_error&) { \ - } \ +#define FAIL_LOAD_ELF(dirname, filename, sectionname) \ + TEST_CASE("Try loading nonexisting program: " dirname "/" filename, "[elf]") { \ + try { \ + read_elf("ebpf-samples/" dirname "/" filename, sectionname, {}, &g_ebpf_platform_linux); \ + REQUIRE(false); \ + } catch (const std::runtime_error&) { \ + } \ } // Some intentional failures @@ -19,13 +19,13 @@ FAIL_LOAD_ELF("cilium", "bpf_lxc.o", "not-found") FAIL_LOAD_ELF("build", "badrelo.o", ".text") FAIL_LOAD_ELF("invalid", "badsymsize.o", "xdp_redirect_map") -#define FAIL_UNMARSHAL(dirname, filename, sectionname) \ - TEST_CASE("Try unmarshalling bad program: " dirname "/" filename " " sectionname, "[unmarshal]") { \ - auto raw_progs = read_elf("ebpf-samples/" dirname "/" filename, sectionname, nullptr, &g_ebpf_platform_linux); \ - REQUIRE(raw_progs.size() == 1); \ - raw_program raw_prog = raw_progs.back(); \ - std::variant prog_or_error = unmarshal(raw_prog); \ - REQUIRE(std::holds_alternative(prog_or_error)); \ +#define FAIL_UNMARSHAL(dirname, filename, sectionname) \ + TEST_CASE("Try unmarshalling bad program: " dirname "/" filename " " sectionname, "[unmarshal]") { \ + auto raw_progs = read_elf("ebpf-samples/" dirname "/" filename, sectionname, {}, &g_ebpf_platform_linux); \ + REQUIRE(raw_progs.size() == 1); \ + raw_program raw_prog = raw_progs.back(); \ + std::variant prog_or_error = unmarshal(raw_prog); \ + REQUIRE(std::holds_alternative(prog_or_error)); \ } // Some intentional unmarshal failures @@ -33,87 +33,87 @@ FAIL_UNMARSHAL("build", "wronghelper.o", "xdp") FAIL_UNMARSHAL("invalid", "invalid-lddw.o", ".text") // Verify a section with only one program in it. -#define VERIFY_SECTION(dirname, filename, sectionname, options, platform, pass) \ - do { \ - auto raw_progs = read_elf("ebpf-samples/" dirname "/" filename, sectionname, nullptr, platform); \ - REQUIRE(raw_progs.size() == 1); \ - raw_program raw_prog = raw_progs.back(); \ - auto prog_or_error = unmarshal(raw_prog); \ - auto prog = std::get_if(&prog_or_error); \ - REQUIRE(prog != nullptr); \ - bool res = ebpf_verify_program(std::cout, *prog, raw_prog.info, options, nullptr); \ - if (pass) \ - REQUIRE(res); \ - else \ - REQUIRE(!res); \ +#define VERIFY_SECTION(dirname, filename, sectionname, options, platform, pass) \ + do { \ + auto raw_progs = read_elf("ebpf-samples/" dirname "/" filename, sectionname, {}, platform); \ + REQUIRE(raw_progs.size() == 1); \ + raw_program raw_prog = raw_progs.back(); \ + auto prog_or_error = unmarshal(raw_prog); \ + auto prog = std::get_if(&prog_or_error); \ + REQUIRE(prog != nullptr); \ + bool res = ebpf_verify_program(std::cout, *prog, raw_prog.info, options, nullptr); \ + if (pass) \ + REQUIRE(res); \ + else \ + REQUIRE(!res); \ } while (0) // Verify a program in a section that may have multiple programs in it. -#define VERIFY_PROGRAM(dirname, filename, section_name, program_name, options, platform, pass) \ - do { \ - auto raw_progs = read_elf("ebpf-samples/" dirname "/" filename, section_name, nullptr, platform); \ - for (auto& raw_prog : raw_progs) { \ - if (raw_prog.function_name == program_name) { \ - auto prog_or_error = unmarshal(raw_prog); \ - auto prog = std::get_if(&prog_or_error); \ - REQUIRE(prog != nullptr); \ - bool res = ebpf_verify_program(std::cout, *prog, raw_prog.info, options, nullptr); \ - if (pass) \ - REQUIRE(res); \ - else \ - REQUIRE(!res); \ - } \ - } \ +#define VERIFY_PROGRAM(dirname, filename, section_name, program_name, options, platform, pass) \ + do { \ + auto raw_progs = read_elf("ebpf-samples/" dirname "/" filename, section_name, {}, platform); \ + for (auto& raw_prog : raw_progs) { \ + if (raw_prog.function_name == program_name) { \ + auto prog_or_error = unmarshal(raw_prog); \ + auto prog = std::get_if(&prog_or_error); \ + REQUIRE(prog != nullptr); \ + bool res = ebpf_verify_program(std::cout, *prog, raw_prog.info, options, nullptr); \ + if (pass) \ + REQUIRE(res); \ + else \ + REQUIRE(!res); \ + } \ + } \ } while (0) -#define TEST_SECTION(project, filename, section) \ - TEST_CASE(project "/" filename " " section, "[verify][samples][" project "]") { \ - VERIFY_SECTION(project, filename, section, nullptr, &g_ebpf_platform_linux, true); \ +#define TEST_SECTION(project, filename, section) \ + TEST_CASE(project "/" filename " " section, "[verify][samples][" project "]") { \ + VERIFY_SECTION(project, filename, section, {}, &g_ebpf_platform_linux, true); \ } -#define TEST_PROGRAM(project, filename, section_name, program_name) \ - TEST_CASE(project "/" filename " " program_name, "[verify][samples][" project "]") { \ - VERIFY_PROGRAM(project, filename, section_name, program_name, nullptr, &g_ebpf_platform_linux, true); \ +#define TEST_PROGRAM(project, filename, section_name, program_name) \ + TEST_CASE(project "/" filename " " program_name, "[verify][samples][" project "]") { \ + VERIFY_PROGRAM(project, filename, section_name, program_name, {}, &g_ebpf_platform_linux, true); \ } -#define TEST_PROGRAM_REJECT(project, filename, section_name, program_name) \ - TEST_CASE(project "/" filename " " program_name, "[verify][samples][" project "]") { \ - VERIFY_PROGRAM(project, filename, section_name, program_name, nullptr, &g_ebpf_platform_linux, false); \ +#define TEST_PROGRAM_REJECT(project, filename, section_name, program_name) \ + TEST_CASE(project "/" filename " " program_name, "[verify][samples][" project "]") { \ + VERIFY_PROGRAM(project, filename, section_name, program_name, {}, &g_ebpf_platform_linux, false); \ } -#define TEST_SECTION_REJECT(project, filename, section) \ - TEST_CASE(project "/" filename " " section, "[verify][samples][" project "]") { \ - VERIFY_SECTION(project, filename, section, nullptr, &g_ebpf_platform_linux, false); \ +#define TEST_SECTION_REJECT(project, filename, section) \ + TEST_CASE(project "/" filename " " section, "[verify][samples][" project "]") { \ + VERIFY_SECTION(project, filename, section, {}, &g_ebpf_platform_linux, false); \ } -#define TEST_SECTION_REJECT_IF_STRICT(project, filename, section) \ - TEST_CASE(project "/" filename " " section, "[verify][samples][" project "]") { \ - ebpf_verifier_options_t options = ebpf_verifier_default_options; \ - VERIFY_SECTION(project, filename, section, &options, &g_ebpf_platform_linux, true); \ - options.strict = true; \ - VERIFY_SECTION(project, filename, section, &options, &g_ebpf_platform_linux, false); \ +#define TEST_SECTION_REJECT_IF_STRICT(project, filename, section) \ + TEST_CASE(project "/" filename " " section, "[verify][samples][" project "]") { \ + ebpf_verifier_options_t options{}; \ + VERIFY_SECTION(project, filename, section, options, &g_ebpf_platform_linux, true); \ + options.strict = true; \ + VERIFY_SECTION(project, filename, section, options, &g_ebpf_platform_linux, false); \ } #define TEST_SECTION_FAIL(project, filename, section) \ TEST_CASE("expect failure " project "/" filename " " section, "[!shouldfail][verify][samples][" project "]") { \ - VERIFY_SECTION(project, filename, section, nullptr, &g_ebpf_platform_linux, true); \ + VERIFY_SECTION(project, filename, section, {}, &g_ebpf_platform_linux, true); \ } #define TEST_SECTION_REJECT_FAIL(project, filename, section) \ TEST_CASE("expect failure " project "/" filename " " section, "[!shouldfail][verify][samples][" project "]") { \ - VERIFY_SECTION(project, filename, section, nullptr, &g_ebpf_platform_linux, false); \ + VERIFY_SECTION(project, filename, section, {}, &g_ebpf_platform_linux, false); \ } -#define TEST_SECTION_LEGACY(dirname, filename, sectionname) \ - TEST_SECTION(dirname, filename, sectionname) \ - TEST_CASE("Fail unmarshalling: " dirname "/" filename " " sectionname, "[unmarshal]") { \ - ebpf_platform_t platform = g_ebpf_platform_linux; \ - platform.supported_conformance_groups &= ~bpf_conformance_groups_t::packet; \ - auto raw_progs = read_elf("ebpf-samples/" dirname "/" filename, sectionname, nullptr, &platform); \ - REQUIRE(raw_progs.size() == 1); \ - raw_program raw_prog = raw_progs.back(); \ - std::variant prog_or_error = unmarshal(raw_prog); \ - REQUIRE(std::holds_alternative(prog_or_error)); \ +#define TEST_SECTION_LEGACY(dirname, filename, sectionname) \ + TEST_SECTION(dirname, filename, sectionname) \ + TEST_CASE("Fail unmarshalling: " dirname "/" filename " " sectionname, "[unmarshal]") { \ + ebpf_platform_t platform = g_ebpf_platform_linux; \ + platform.supported_conformance_groups &= ~bpf_conformance_groups_t::packet; \ + auto raw_progs = read_elf("ebpf-samples/" dirname "/" filename, sectionname, {}, &platform); \ + REQUIRE(raw_progs.size() == 1); \ + raw_program raw_prog = raw_progs.back(); \ + std::variant prog_or_error = unmarshal(raw_prog); \ + REQUIRE(std::holds_alternative(prog_or_error)); \ } TEST_SECTION("bpf_cilium_test", "bpf_lxc_jit.o", "1/0xdc06") @@ -501,7 +501,7 @@ TEST_SECTION("raw_tracepoint/filler/proc_startupdate_2") TEST_SECTION("raw_tracepoint/filler/sys_recvfrom_x") */ TEST_PROGRAM_REJECT("build", "bpf2bpf.o", ".text", "plus1"); // Subprogram will fail verification. -TEST_PROGRAM("build", "bpf2bpf.o", "test", "func"); // Subprogram can be called from main program. +TEST_PROGRAM("build", "bpf2bpf.o", "test", "func"); // Subprogram can be called from main program. TEST_SECTION("build", "byteswap.o", ".text") TEST_SECTION("build", "stackok.o", ".text") TEST_SECTION("build", "packet_start_ok.o", "xdp") @@ -587,12 +587,12 @@ TEST_SECTION_FAIL("cilium", "bpf_xdp_snat_linux.o", "2/16") TEST_SECTION_FAIL("linux", "test_map_in_map_kern.o", "kprobe/sys_connect") void test_analyze_thread(const cfg_t* cfg, program_info* info, bool* res) { - *res = run_ebpf_analysis(std::cout, *cfg, *info, nullptr, nullptr); + *res = run_ebpf_analysis(std::cout, *cfg, *info, {}, nullptr); } // Test multithreading TEST_CASE("multithreading", "[verify][multithreading]") { - auto raw_progs1 = read_elf("ebpf-samples/bpf_cilium_test/bpf_netdev.o", "2/1", nullptr, &g_ebpf_platform_linux); + auto raw_progs1 = read_elf("ebpf-samples/bpf_cilium_test/bpf_netdev.o", "2/1", {}, &g_ebpf_platform_linux); REQUIRE(raw_progs1.size() == 1); raw_program raw_prog1 = raw_progs1.back(); auto prog_or_error1 = unmarshal(raw_prog1); @@ -600,7 +600,7 @@ TEST_CASE("multithreading", "[verify][multithreading]") { REQUIRE(prog1 != nullptr); const cfg_t cfg1 = prepare_cfg(*prog1, raw_prog1.info); - auto raw_progs2 = read_elf("ebpf-samples/bpf_cilium_test/bpf_netdev.o", "2/2", nullptr, &g_ebpf_platform_linux); + auto raw_progs2 = read_elf("ebpf-samples/bpf_cilium_test/bpf_netdev.o", "2/2", {}, &g_ebpf_platform_linux); REQUIRE(raw_progs2.size() == 1); raw_program raw_prog2 = raw_progs2.back(); auto prog_or_error2 = unmarshal(raw_prog2);