Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified scripts/format-code
100644 → 100755
Empty file.
12 changes: 3 additions & 9 deletions src/ir/assertions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,19 +110,13 @@ class AssertExtractor {
res.emplace_back(a);
}
break;
case ArgSingle::Kind::PTR_TO_SOCKET:
res.emplace_back(TypeConstraint{arg.reg, TypeGroup::socket});
break;
case ArgSingle::Kind::PTR_TO_BTF_ID:
res.emplace_back(TypeConstraint{arg.reg, TypeGroup::btf_id});
break;
case ArgSingle::Kind::PTR_TO_SOCKET: res.emplace_back(TypeConstraint{arg.reg, TypeGroup::socket}); break;
case ArgSingle::Kind::PTR_TO_BTF_ID: res.emplace_back(TypeConstraint{arg.reg, TypeGroup::btf_id}); break;
case ArgSingle::Kind::PTR_TO_ALLOC_MEM:
res.emplace_back(TypeConstraint{arg.reg, TypeGroup::alloc_mem});
break;
case ArgSingle::Kind::PTR_TO_SPIN_LOCK:
case ArgSingle::Kind::PTR_TO_TIMER:
res.emplace_back(TypeConstraint{arg.reg, TypeGroup::mem});
break;
case ArgSingle::Kind::PTR_TO_TIMER: res.emplace_back(TypeConstraint{arg.reg, TypeGroup::mem}); break;
case ArgSingle::Kind::CONST_SIZE_OR_ZERO:
res.emplace_back(TypeConstraint{arg.reg, TypeGroup::number});
res.emplace_back(ValidSize{arg.reg, true});
Expand Down
7 changes: 4 additions & 3 deletions src/ir/unmarshal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,8 +390,8 @@ struct Unmarshaller {
}

[[nodiscard]]
auto makeLddw(const EbpfInst inst, const int32_t next_imm, const vector<EbpfInst>& insts,
const Pc pc) const -> Instruction {
auto makeLddw(const EbpfInst inst, const int32_t next_imm, const vector<EbpfInst>& insts, const Pc pc) const
-> Instruction {
if (pc >= insts.size() - 1) {
throw InvalidInstruction(pc, "incomplete lddw");
}
Expand Down Expand Up @@ -502,7 +502,8 @@ struct Unmarshaller {
};
const auto return_info = classify_call_return_type(proto.return_type);
if (!return_info.has_value()) {
return mark_unsupported(std::string("helper prototype is unavailable on this platform: ") + helper_prototype_name);
return mark_unsupported(std::string("helper prototype is unavailable on this platform: ") +
helper_prototype_name);
}
res.return_ptr_type = return_info->pointer_type;
res.return_nullable = return_info->pointer_nullable;
Expand Down
122 changes: 85 additions & 37 deletions src/test/test_verify.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
#pragma once

#include <catch2/catch_all.hpp>
#include <cstddef>
#include <cstdint>
#include <mutex>
#include <ranges>
#include <string>
Expand All @@ -14,7 +12,6 @@

#include "ebpf_verifier.hpp"
#include "io/elf_loader.hpp"
#include "linux/gpl/spec_type_descriptors.hpp"

namespace verify_test {

Expand Down Expand Up @@ -68,7 +65,7 @@ inline const char* to_string(VerifyIssueKind kind) noexcept {
}

template <typename T>
inline void hash_combine(size_t& seed, const T& value) noexcept {
void hash_combine(size_t& seed, const T& value) noexcept {
seed ^= std::hash<T>{}(value) + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2);
}

Expand Down Expand Up @@ -98,7 +95,7 @@ inline std::vector<prevail::RawProgram> read_elf_cached(const std::string& path,
static std::mutex cache_mutex;
static std::unordered_map<ElfObjectCacheKey, prevail::ElfObject, ElfObjectCacheKeyHash> object_cache;

ElfObjectCacheKey key{
const ElfObjectCacheKey key{
.path = path,
.platform = platform,
.verbosity_print_line_info = options.verbosity_opts.print_line_info,
Expand All @@ -113,8 +110,57 @@ inline std::vector<prevail::RawProgram> read_elf_cached(const std::string& path,
return it->second.get_programs(desired_section, desired_program);
}

// Compile-time test name truncation. Catch2's TEST_CASE accepts any const char*,
// so we can pass .data from a constexpr char array instead of a string literal.
// When the name exceeds MAX_LEN, we truncate and append a 4-digit hex hash of the
// full name to preserve uniqueness.
struct BoundedTestName {
static constexpr size_t MAX_LEN = 75;
char data[MAX_LEN + 1]{};

template <size_t N>
explicit constexpr BoundedTestName(const char (&str)[N]) {
if constexpr (N - 1 <= MAX_LEN) {
for (size_t i = 0; i < N; ++i) {
data[i] = str[i];
}
} else {
// FNV-1a hash of full name for a unique suffix.
uint32_t hash = 2166136261u;
for (size_t i = 0; i < N - 1; ++i) {
hash ^= static_cast<unsigned char>(str[i]);
hash *= 16777619u;
}
// Format: first (MAX_LEN - 6) chars + "..xxxx"
constexpr size_t prefix_len = MAX_LEN - 6;
for (size_t i = 0; i < prefix_len; ++i) {
data[i] = str[i];
}
data[prefix_len] = '.';
data[prefix_len + 1] = '.';
constexpr char hex[] = "0123456789abcdef";
data[prefix_len + 2] = hex[(hash >> 12) & 0xf];
data[prefix_len + 3] = hex[(hash >> 8) & 0xf];
data[prefix_len + 4] = hex[(hash >> 4) & 0xf];
data[prefix_len + 5] = hex[hash & 0xf];
data[MAX_LEN] = '\0';
}
}
};

} // namespace verify_test

// BOUNDED_TEST_CASE: like TEST_CASE, but truncates names exceeding MAX_LEN characters.
// Uses __LINE__ to generate a unique constexpr variable name; both expansions of
// PREVAIL_BOUNDED_NAME within one macro invocation share the same __LINE__.
#define PREVAIL_CONCAT_IMPL(a, b) a##b
#define PREVAIL_CONCAT(a, b) PREVAIL_CONCAT_IMPL(a, b)
#define PREVAIL_BOUNDED_NAME PREVAIL_CONCAT(_prevail_btn_, __LINE__)

#define BOUNDED_TEST_CASE(name_literal, tags) \
static constexpr verify_test::BoundedTestName PREVAIL_BOUNDED_NAME(name_literal); \
TEST_CASE(PREVAIL_BOUNDED_NAME.data, tags)

// Verify a program in a section that may have multiple programs in it.
#define VERIFY_PROGRAM(dirname, filename, section_name, program_name, _options, platform, should_pass, count) \
do { \
Expand Down Expand Up @@ -162,88 +208,90 @@ inline std::vector<prevail::RawProgram> read_elf_cached(const std::string& path,
VERIFY_PROGRAM(dirname, filename, section_name, "", _options, platform, should_pass, 1)

#define TEST_SECTION(project, filename, section) \
TEST_CASE(project "/" filename " " section, "[verify][samples][" project "]") { \
BOUNDED_TEST_CASE(project "/" filename " " section, "[verify][samples][" project "]") { \
VERIFY_SECTION(project, filename, section, {}, &prevail::g_ebpf_platform_linux, true); \
}

#define TEST_SECTION_SLOW(project, filename, section) \
TEST_CASE(project "/" filename " " section, "[verify][samples][slow][" project "]") { \
VERIFY_SECTION(project, filename, section, {}, &prevail::g_ebpf_platform_linux, true); \
#define TEST_SECTION_SLOW(project, filename, section) \
BOUNDED_TEST_CASE(project "/" filename " " section, "[verify][samples][slow][" project "]") { \
VERIFY_SECTION(project, filename, section, {}, &prevail::g_ebpf_platform_linux, true); \
}

#define TEST_PROGRAM(project, filename, section_name, program_name, count) \
TEST_CASE(project "/" filename " " program_name, "[verify][samples][" project "]") { \
BOUNDED_TEST_CASE(project "/" filename " " program_name, "[verify][samples][" project "]") { \
VERIFY_PROGRAM(project, filename, section_name, program_name, {}, &prevail::g_ebpf_platform_linux, true, \
count); \
}

#define TEST_PROGRAM_FAIL(project, filename, section_name, program_name, count, kind) \
TEST_CASE(project "/" filename " " program_name, "[!shouldfail][verify][samples][" project "]") { \
BOUNDED_TEST_CASE(project "/" filename " " program_name, "[!shouldfail][verify][samples][" project "]") { \
INFO("issue_kind=" << verify_test::to_string(kind)); \
VERIFY_PROGRAM(project, filename, section_name, program_name, {}, &prevail::g_ebpf_platform_linux, true, \
count); \
}

#define TEST_PROGRAM_REJECT(project, filename, section_name, program_name, count) \
TEST_CASE(project "/" filename " " program_name, "[verify][samples][" project "]") { \
BOUNDED_TEST_CASE(project "/" filename " " program_name, "[verify][samples][" project "]") { \
VERIFY_PROGRAM(project, filename, section_name, program_name, {}, &prevail::g_ebpf_platform_linux, false, \
count); \
}

#define TEST_PROGRAM_REJECT_FAIL(project, filename, section_name, program_name, count) \
TEST_CASE(project "/" filename " " program_name, "[!shouldfail][verify][samples][" project "]") { \
BOUNDED_TEST_CASE(project "/" filename " " program_name, "[!shouldfail][verify][samples][" project "]") { \
VERIFY_PROGRAM(project, filename, section_name, program_name, {}, &prevail::g_ebpf_platform_linux, false, \
count); \
}

#define TEST_SECTION_REJECT(project, filename, section) \
TEST_CASE(project "/" filename " " section, "[verify][samples][" project "]") { \
BOUNDED_TEST_CASE(project "/" filename " " section, "[verify][samples][" project "]") { \
VERIFY_SECTION(project, filename, section, {}, &prevail::g_ebpf_platform_linux, false); \
}

#define TEST_SECTION_REJECT_IF_STRICT(project, filename, section) \
TEST_CASE(project "/" filename " " section, "[verify][samples][" project "]") { \
BOUNDED_TEST_CASE(project "/" filename " " section, "[verify][samples][" project "]") { \
prevail::ebpf_verifier_options_t options{}; \
VERIFY_SECTION(project, filename, section, options, &prevail::g_ebpf_platform_linux, true); \
options.strict = true; \
VERIFY_SECTION(project, filename, section, options, &prevail::g_ebpf_platform_linux, false); \
}

#define TEST_SECTION_FAIL(project, filename, section, kind) \
TEST_CASE("expect failure " project "/" filename " " section, "[!shouldfail][verify][samples][" project "]") { \
INFO("issue_kind=" << verify_test::to_string(kind)); \
VERIFY_SECTION(project, filename, section, {}, &prevail::g_ebpf_platform_linux, true); \
#define TEST_SECTION_FAIL(project, filename, section, kind) \
BOUNDED_TEST_CASE("expect failure " project "/" filename " " section, \
"[!shouldfail][verify][samples][" project "]") { \
INFO("issue_kind=" << verify_test::to_string(kind)); \
VERIFY_SECTION(project, filename, section, {}, &prevail::g_ebpf_platform_linux, true); \
}

#define TEST_SECTION_SKIP(project, filename, section, kind) \
TEST_CASE(project "/" filename " " section, "[verify][samples][" project "]") { \
SKIP(verify_test::to_string(kind)); \
#define TEST_SECTION_SKIP(project, filename, section, kind) \
BOUNDED_TEST_CASE(project "/" filename " " section, "[verify][samples][" project "]") { \
SKIP(verify_test::to_string(kind)); \
}

#define TEST_PROGRAM_SKIP(project, filename, section_name, program_name, kind) \
TEST_CASE(project "/" filename " " program_name, "[verify][samples][" project "]") { \
SKIP(verify_test::to_string(kind)); \
#define TEST_PROGRAM_SKIP(project, filename, section_name, program_name, kind) \
BOUNDED_TEST_CASE(project "/" filename " " program_name, "[verify][samples][" project "]") { \
SKIP(verify_test::to_string(kind)); \
}

#define TEST_SECTION_REJECT_LOAD(project, filename, section) \
TEST_CASE("expect load rejection " project "/" filename " " section, "[verify][samples][" project "]") { \
REQUIRE_THROWS_AS(([&]() { \
(void)verify_test::read_elf_cached("ebpf-samples/" project "/" filename, section, "", \
{}, &prevail::g_ebpf_platform_linux); \
}()), \
std::runtime_error); \
#define TEST_SECTION_REJECT_LOAD(project, filename, section) \
BOUNDED_TEST_CASE("expect load rejection " project "/" filename " " section, "[verify][samples][" project "]") { \
REQUIRE_THROWS_AS(([&]() { \
(void)verify_test::read_elf_cached("ebpf-samples/" project "/" filename, section, "", \
{}, &prevail::g_ebpf_platform_linux); \
}()), \
std::runtime_error); \
}

#define TEST_SECTION_FAIL_SLOW(project, filename, section, kind) \
TEST_CASE("expect failure " project "/" filename " " section, \
"[!shouldfail][verify][samples][slow][" project "]") { \
BOUNDED_TEST_CASE("expect failure " project "/" filename " " section, \
"[!shouldfail][verify][samples][slow][" project "]") { \
INFO("issue_kind=" << verify_test::to_string(kind)); \
VERIFY_SECTION(project, filename, section, {}, &prevail::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, {}, &prevail::g_ebpf_platform_linux, false); \
#define TEST_SECTION_REJECT_FAIL(project, filename, section) \
BOUNDED_TEST_CASE("expect failure " project "/" filename " " section, \
"[!shouldfail][verify][samples][" project "]") { \
VERIFY_SECTION(project, filename, section, {}, &prevail::g_ebpf_platform_linux, false); \
}

#define TEST_SECTION_LEGACY(dirname, filename, sectionname) TEST_SECTION(dirname, filename, sectionname)
Expand Down
Loading