From 7583705419176d0194ca835954b0599e3a32ccae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20L=C3=B3pez=20Jaimez?= Date: Fri, 3 Oct 2025 22:28:13 +0000 Subject: [PATCH 1/4] refactor the kcov collection to make it more efficient --- ebpf_ffi/cbpf.cc | 30 ++----- ebpf_ffi/cbpf.h | 6 +- ebpf_ffi/ebpf.cc | 28 ++---- ebpf_ffi/ebpf.h | 4 +- ebpf_ffi/ffi.cc | 154 +++++++++++++++++++-------------- ebpf_ffi/ffi.h | 21 +++-- main.go | 3 +- pkg/units/control.go | 4 + pkg/units/ffi.go | 33 +++---- pkg/units/metrics_unit.go | 27 +----- pkg/units/metrics_unit_test.go | 9 -- 11 files changed, 150 insertions(+), 169 deletions(-) diff --git a/ebpf_ffi/cbpf.cc b/ebpf_ffi/cbpf.cc index fc6e1b5..8427cb7 100644 --- a/ebpf_ffi/cbpf.cc +++ b/ebpf_ffi/cbpf.cc @@ -19,6 +19,7 @@ #include #include #include +#include "ffi.h" bool load_cbpf_program(void *prog_buff, size_t size, std::string &error, int *socks) { @@ -62,38 +63,25 @@ struct bpf_result validation_error(std::string error_message, return serialize_proto(*vres); } -struct bpf_result ffi_load_cbpf_program(void *prog_buff, size_t size, - int coverage_enabled, - uint64_t coverage_size) { +struct bpf_result ffi_load_cbpf_program(void *prog_buff, size_t size) { std::string error_message; - struct coverage_data cover; - memset(&cover, 0, sizeof(struct coverage_data)); - cover.fd = -1; - cover.coverage_size = coverage_size; - if (coverage_enabled) enable_coverage(&cover); - ValidationResult vres; int socks[2] = {-1, -1}; - if (!load_cbpf_program(prog_buff, size, error_message, socks)) { - // Return why we failed to load the program. - if (coverage_enabled) get_coverage_and_free_resources(&cover, &vres); + bool coverage_enabled = enable_coverage(); + bool cbpf_loaded = load_cbpf_program(prog_buff, size, error_message, socks); + if (coverage_enabled) { + disable_coverage(); + get_coverage(&vres); + } + if (!cbpf_loaded) { return validation_error(error_message, &vres); } - if (coverage_enabled) get_coverage_and_free_resources(&cover, &vres); - // Start building the validation result proto. vres.set_socket_write(socks[0]); vres.set_socket_read(socks[1]); - if (cover.fd != -1) { - vres.set_did_collect_coverage(true); - vres.set_coverage_size(cover.coverage_size); - vres.set_coverage_buffer(reinterpret_cast(cover.coverage_buffer)); - } else { - vres.set_did_collect_coverage(false); - } if (socks[0] < 0) { // Return why we failed to load the program. diff --git a/ebpf_ffi/cbpf.h b/ebpf_ffi/cbpf.h index c1bc374..6a647c7 100644 --- a/ebpf_ffi/cbpf.h +++ b/ebpf_ffi/cbpf.h @@ -17,7 +17,7 @@ #ifndef EBPF_FUZZER_EBPF_FFI_CBPF_H_ #define EBPF_FUZZER_EBPF_FFI_CBPF_H_ -#include "ffi.h" +#include "ebpf_ffi/ffi.h" extern "C" { @@ -29,9 +29,7 @@ bool load_cbpf_program(void *prog_buff, size_t size, std::string &error, // Loads a bpf program specified by |prog_buff| with |size| and returns struct // with a serialized ValidationResult proto. -struct bpf_result ffi_load_cbpf_program(void *prog_buff, size_t size, - int coverage_enabled, - uint64_t coverage_size); +struct bpf_result ffi_load_cbpf_program(void *prog_buff, size_t size); bool execute_cbpf_program(int prog_fd, uint8_t *input, uint8_t *output, int input_length, std::string &error_message); diff --git a/ebpf_ffi/ebpf.cc b/ebpf_ffi/ebpf.cc index 7651d10..156856c 100644 --- a/ebpf_ffi/ebpf.cc +++ b/ebpf_ffi/ebpf.cc @@ -115,40 +115,30 @@ ValidationResult load_ebpf_program(EncodedProgram program, std::string &error) { return res; } -struct bpf_result ffi_load_ebpf_program(void *serialized_proto, size_t size, - int coverage_enabled, - uint64_t coverage_size) { +struct bpf_result ffi_load_ebpf_program(void *serialized_proto, size_t size) { std::string error_message; - struct coverage_data cover; - memset(&cover, 0, sizeof(struct coverage_data)); - cover.fd = -1; - cover.coverage_size = coverage_size; - if (coverage_enabled) enable_coverage(&cover); - std::string serialized_proto_string( reinterpret_cast(serialized_proto), size); EncodedProgram program; if (!program.ParseFromString(serialized_proto_string)) { error_message = "Could not parse EncodedProgram proto"; } + + bool coverage_enabled = enable_coverage(); ValidationResult vres = load_ebpf_program(program, error_message); - if (coverage_enabled) get_coverage_and_free_resources(&cover, &vres); - - if (cover.fd != -1) { - vres.set_did_collect_coverage(true); - vres.set_coverage_size(cover.coverage_size); - vres.set_coverage_buffer(reinterpret_cast(cover.coverage_buffer)); - } else { - vres.set_did_collect_coverage(false); + vres.set_did_collect_coverage(false); + if (coverage_enabled) { + get_coverage(&vres); + disable_coverage(); } + + vres.set_is_valid(true); if (vres.program_fd() < 0) { // Return why we failed to load the program. vres.set_bpf_error(error_message); vres.set_is_valid(false); - } else { - vres.set_is_valid(true); } return serialize_proto(vres); diff --git a/ebpf_ffi/ebpf.h b/ebpf_ffi/ebpf.h index 57fa3b2..ce309c8 100644 --- a/ebpf_ffi/ebpf.h +++ b/ebpf_ffi/ebpf.h @@ -34,9 +34,7 @@ ValidationResult load_ebpf_program(EncodedProgram program, std::string &error); // Loads a bpf program specified by |prog_buff| with |size| and returns struct // with a serialized ValidationResult proto. -struct bpf_result ffi_load_ebpf_program(void *serialized_proto, size_t size, - int coverage_enabled, - uint64_t coverage_size); +struct bpf_result ffi_load_ebpf_program(void *serialized_proto, size_t size); bool get_map_elements(int map_fd, size_t map_size, std::vector *res, std::string &error); diff --git a/ebpf_ffi/ffi.cc b/ebpf_ffi/ffi.cc index 51f7c3e..c785b02 100644 --- a/ebpf_ffi/ffi.cc +++ b/ebpf_ffi/ffi.cc @@ -15,10 +15,8 @@ #include "ebpf_ffi/ffi.h" #include -#include #include #include -#include #include #include #include @@ -26,10 +24,10 @@ #include #include -#include #include "absl/container/flat_hash_set.h" #include "absl/strings/escaping.h" +#include "ffi.h" #include "google/protobuf/message.h" #include "google/protobuf/repeated_field.h" #include "proto/ffi.pb.h" @@ -46,79 +44,103 @@ using ebpf_fuzzer::ExecutionResult; using ebpf_fuzzer::MapElements; using ebpf_fuzzer::ValidationResult; +struct coverage_data* kCoverageData = nullptr; + // All the functions in this extern are FFIs intended to be invoked from go. extern "C" { -bpf_result serialize_proto(const google::protobuf::Message &proto) { - std::string proto_encoded; - absl::Base64Escape(proto.SerializeAsString(), &proto_encoded); + bpf_result serialize_proto(const google::protobuf::Message &proto) { + std::string proto_encoded; + absl::Base64Escape(proto.SerializeAsString(), &proto_encoded); - // The memory for this string will be freed by the Go program. - char *serialized_proto = - reinterpret_cast(malloc(proto_encoded.size() + 1)); - strncpy(serialized_proto, proto_encoded.c_str(), proto_encoded.size()); + // The memory for this string will be freed by the Go program. + char *serialized_proto = + reinterpret_cast(malloc(proto_encoded.size() + 1)); + strncpy(serialized_proto, proto_encoded.c_str(), proto_encoded.size()); - struct bpf_result res; - res.serialized_proto = serialized_proto; - res.size = proto_encoded.size(); - return res; -} + struct bpf_result res; + res.serialized_proto = serialized_proto; + res.size = proto_encoded.size(); + return res; + } -void enable_coverage(struct coverage_data *coverage_info) { - int fd = open("/sys/kernel/debug/kcov", O_RDWR); - if (fd == -1) return; - /* Setup trace mode and trace size. */ - if (ioctl(fd, KCOV_INIT_TRACE, coverage_info->coverage_size)) return; - /* Mmap buffer shared between kernel- and user-space. */ - uint64_t *cover = - (uint64_t *)mmap(nullptr, coverage_info->coverage_size * sizeof(uint64_t), - PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if ((void *)cover == MAP_FAILED) return; - /* Enable coverage collection on the current thread. */ - if (ioctl(fd, KCOV_ENABLE, KCOV_TRACE_PC)) return; - /* Reset coverage from the tail of the ioctl() call. */ - __atomic_store_n(&cover[0], 0, __ATOMIC_RELAXED); - coverage_info->fd = fd; - coverage_info->coverage_buffer = cover; -} + void get_coverage(ValidationResult *vres) { + if (kCoverageData == nullptr || kCoverageData->fd == -1) return; + uint64_t trace_size = kCoverageData->coverage_buffer[0]; + + auto *coverage_addresses = vres->mutable_coverage_address(); + absl::flat_hash_set seen_address; + for (uint64_t i = 0; i < trace_size; i++) { + uint64_t addr = kCoverageData->coverage_buffer[i + 1]; + if (seen_address.find(addr) == seen_address.end()) { + coverage_addresses->Add(addr); + seen_address.insert(addr); + } + } + vres->set_did_collect_coverage(true); + vres->set_coverage_size(trace_size); + return; + } -void get_coverage_and_free_resources(struct coverage_data *cstruct, - ValidationResult *vres) { - if (cstruct->fd == -1) return; - uint64_t trace_size = - __atomic_load_n(&cstruct->coverage_buffer[0], __ATOMIC_RELAXED); - - auto *coverage_addresses = vres->mutable_coverage_address(); - absl::flat_hash_set seen_address; - for (uint64_t i = 0; i < trace_size; i++) { - uint64_t addr = cstruct->coverage_buffer[i + 1]; - if (seen_address.find(addr) == seen_address.end()) { - coverage_addresses->Add(cstruct->coverage_buffer[i + 1]); - seen_address.insert(addr); + bool enable_coverage() { + if (!kCoverageData || kCoverageData->fd == -1) return false; + return ioctl(kCoverageData->fd, KCOV_ENABLE, KCOV_TRACE_PC) == 0; } - } - ioctl(cstruct->fd, KCOV_DISABLE, 0); - close(cstruct->fd); - munmap(cstruct->coverage_buffer, cstruct->coverage_size * sizeof(uint64_t)); -} + void disable_coverage() { + if (kCoverageData == nullptr || kCoverageData->fd == -1) return; + (void)ioctl(kCoverageData->fd, KCOV_DISABLE, 0); + } -bool execute_error(std::string &error_message, const char *strerr, - int *sockets) { - if (sockets != nullptr) { - close(sockets[0]); - close(sockets[1]); - } - error_message = strerr; - return false; -} + int ffi_setup_coverage() { + if (!kCoverageData) { + kCoverageData = (struct coverage_data*)malloc(sizeof(struct coverage_data)); + memset(kCoverageData, 0, sizeof(struct coverage_data)); + } + + int fd = open("/sys/kernel/debug/kcov", O_RDWR); + kCoverageData->fd = fd; + if (fd == -1) return -1; + /* Setup trace mode and trace size. */ + if (ioctl(fd, KCOV_INIT_TRACE, KCOV_SIZE)) return -1; + /* Mmap buffer shared between kernel- and user-space. */ + uint64_t *cover = + (uint64_t *)mmap(nullptr, KCOV_SIZE * sizeof(uint64_t), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if ((void *)cover == MAP_FAILED) return -1; + memset(cover, 0, KCOV_SIZE * sizeof(uint64_t)); + + kCoverageData->fd = fd; + kCoverageData->coverage_buffer = cover; + return 0; + } -struct bpf_result return_error(std::string error_message, - ExecutionResult *result) { - result->set_did_succeed(false); - result->set_error_message(error_message); - return serialize_proto(*result); -} + int ffi_cleanup_coverage() { + if (!kCoverageData) return 0; + + close(kCoverageData->fd); + munmap(kCoverageData->coverage_buffer, KCOV_SIZE * sizeof(uint64_t)); + free(kCoverageData); + kCoverageData = nullptr; + return 0; + } + + bool execute_error(std::string &error_message, const char *strerr, + int *sockets) { + if (sockets != nullptr) { + close(sockets[0]); + close(sockets[1]); + } + error_message = strerr; + return false; + } + + struct bpf_result return_error(std::string error_message, + ExecutionResult *result) { + result->set_did_succeed(false); + result->set_error_message(error_message); + return serialize_proto(*result); + } -void ffi_close_fd(int prog_fd) { close(prog_fd); } + void ffi_close_fd(int prog_fd) { close(prog_fd); } } diff --git a/ebpf_ffi/ffi.h b/ebpf_ffi/ffi.h index 5fdf37a..d08d5ae 100644 --- a/ebpf_ffi/ffi.h +++ b/ebpf_ffi/ffi.h @@ -48,6 +48,9 @@ #define KCOV_TRACE_PC 0 #define KCOV_TRACE_CMP 1 +// 64mb for kcov coverage. +#define KCOV_SIZE 1024 * 1024 * 64 + using ebpf_fuzzer::CbpfExecutionRequest; using ebpf_fuzzer::EncodedProgram; using ebpf_fuzzer::ExecutionRequest; @@ -66,11 +69,6 @@ struct bpf_result { bpf_result serialize_proto(const google::protobuf::Message &proto); -void enable_coverage(struct coverage_data *coverage_info); - -void get_coverage_and_free_resources(struct coverage_data *cstruct, - ValidationResult *vres); - bool execute_error(std::string &error_message, const char *strerr, int *sockets); @@ -83,10 +81,23 @@ int ffi_create_bpf_map(size_t size); // Closes the given file descriptor, this is to free up resources. void ffi_close_fd(int fd); +// Enable kcov coverage. +int ffi_setup_coverage(); + +// Disble kcov coverage. +int ffi_cleanup_coverage(); + +bool enable_coverage(); +void disable_coverage(); +void get_coverage(ValidationResult *vres); + struct coverage_data { int fd; uint64_t coverage_size; uint64_t *coverage_buffer; }; + +extern struct coverage_data* kCoverageData; } + #endif // EBPF_FUZZER_EBPF_FFI_FFI_H_ diff --git a/main.go b/main.go index cf0cc53..15d42c1 100644 --- a/main.go +++ b/main.go @@ -27,7 +27,6 @@ import ( // Flags that the binary can accept. var ( - coverageBufferSize = flag.Uint64("coverage_buffer_size", 64<<20, "Size of the buffer passed to kcov to get coverage addresses, the higher the number, the slower coverage collection will be") metricsThreshold = flag.Int("metrics_threshold", 200, "Collect detailed metrics (coverage) every `metrics_threshold` validated programs") strategyName = flag.String("strategy", "playground", "Strategy to use for fuzzing") vmLinuxPath = flag.String("vmlinux_path", "/root/vmlinux", "Path to the linux image that will be passed to addr2line to get coverage info") @@ -79,7 +78,7 @@ func main() { }) controlUnit := units.Control{} - metricsUnit := units.NewMetricsUnit(*metricsThreshold, *coverageBufferSize, *vmLinuxPath, *sourceFilesPath, *metricsServerAddr, uint16(*metricsServerPort), coverageManager) + metricsUnit := units.NewMetricsUnit(*metricsThreshold, *vmLinuxPath, *sourceFilesPath, *metricsServerAddr, uint16(*metricsServerPort), coverageManager) if err := controlUnit.Init(&units.FFI{ MetricsUnit: metricsUnit, diff --git a/pkg/units/control.go b/pkg/units/control.go index 32e42dd..0ed4a89 100644 --- a/pkg/units/control.go +++ b/pkg/units/control.go @@ -86,6 +86,10 @@ func (cu *Control) IsReady() bool { // RunFuzzer kickstars the fuzzer in the mode that was specified at Init time. func (cu *Control) RunFuzzer() error { + cu.ffi.InitKcov() + defer func(){ + cu.ffi.CleanupKcov() + }() for !cu.strat.IsFuzzingDone() { prog, err := cu.strat.GenerateProgram(cu.ffi) if err != nil { diff --git a/pkg/units/ffi.go b/pkg/units/ffi.go index f6a78ca..e8750a4 100644 --- a/pkg/units/ffi.go +++ b/pkg/units/ffi.go @@ -21,9 +21,9 @@ package units // char* serialized_proto; // size_t size; //}; -//struct bpf_result ffi_load_cbpf_program(void* prog_buff, size_t size, int coverage_enabled, unsigned long coverage_size); +//struct bpf_result ffi_load_cbpf_program(void* prog_buff, size_t size); //struct bpf_result ffi_execute_cbpf_program(void* serialized_proto, size_t length); -//struct bpf_result ffi_load_ebpf_program(void* serialized_proto, size_t size, int coverage_enabled, unsigned long coverage_size); +//struct bpf_result ffi_load_ebpf_program(void* serialized_proto, size_t size); //struct bpf_result ffi_execute_ebpf_program(void* serialized_proto, size_t length); //struct bpf_result ffi_get_map_elements(int map_fd, uint64_t map_size); //struct bpf_result ffi_get_map_elements_fd_array(uint64_t fd_array_addr, uint32_t idx, uint64_t map_size); @@ -31,6 +31,8 @@ package units //void ffi_close_fd(int fd); //int ffi_update_map_element(int map_fd, int key, uint64_t value); //void ffi_clean_fd_array(unsigned long long int addr, int size); +//int ffi_setup_coverage(); +//int ffi_cleanup_coverage(); import "C" import ( @@ -157,14 +159,8 @@ func (e *FFI) ValidateEbpfProgram(encodedProgram *fpb.EncodedProgram) (*fpb.Vali if len(encodedProgram.Program) == 0 && encodedProgram != nil { return nil, fmt.Errorf("cannot run empty program") } - shouldCollect, coverageSize := e.MetricsUnit.ShouldGetCoverage() - cbool := 0 - if shouldCollect { - cbool = 1 - } serializedProto, err := proto.Marshal(encodedProgram) - bpfVerifyResult := C.ffi_load_ebpf_program(unsafe.Pointer(&serializedProto[0]), C.ulong(len(serializedProto)), - C.int(cbool), C.ulong(coverageSize)) + bpfVerifyResult := C.ffi_load_ebpf_program(unsafe.Pointer(&serializedProto[0]), C.ulong(len(serializedProto))) res, err := validationProtoFromStruct(&bpfVerifyResult) if err != nil { return nil, err @@ -191,12 +187,7 @@ func (e *FFI) ValidateCbpfProgram(prog []cbpf.Filter) (*fpb.ValidationResult, er if len(prog) == 0 { return nil, fmt.Errorf("cannot run empty program") } - shouldCollect, coverageSize := e.MetricsUnit.ShouldGetCoverage() - cbool := 0 - if shouldCollect { - cbool = 1 - } - bpfVerifyResult := C.ffi_load_cbpf_program(unsafe.Pointer(&prog[0]), C.ulong(len(prog)), C.int(cbool) /*coverage_size=*/, C.ulong(coverageSize)) + bpfVerifyResult := C.ffi_load_cbpf_program(unsafe.Pointer(&prog[0]), C.ulong(len(prog))) res, err := validationProtoFromStruct(&bpfVerifyResult) if err != nil { return nil, err @@ -214,3 +205,15 @@ func (e *FFI) RunCbpfProgram(executionRequest *fpb.CbpfExecutionRequest) (*fpb.E res := C.ffi_execute_cbpf_program(unsafe.Pointer(&serializedProto[0]), C.ulong(len(serializedProto))) return executionProtoFromStruct(&res) } + +// InitKcov sets up all the required kcov structures. +func (e *FFI) InitKcov() { + if C.ffi_setup_coverage() != 0 { + fmt.Println("could not setup coverage correctly") + } +} + +// CleanupKcov destroys all the resources created for kcov. +func (e *FFI) CleanupKcov() { + C.ffi_cleanup_coverage() +} diff --git a/pkg/units/metrics_unit.go b/pkg/units/metrics_unit.go index 46c4f19..3777438 100644 --- a/pkg/units/metrics_unit.go +++ b/pkg/units/metrics_unit.go @@ -35,11 +35,6 @@ type Metrics struct { // the Metrics will collect detailed info (coverage, and verifier logs). SamplingThreshold int - // KCovSize represents the size of the coverage sample that kcov will - // collect, the bigger the sample the slower collecting coverage - // will be (but the more precies). - KCovSize uint64 - isKCovSupported bool // Since Processing coverage is a slow operation, we put all the @@ -95,28 +90,11 @@ func (mu *Metrics) validationResultProcessingRoutine() { } } -// ShouldGetCoverage has two purposes: record that a program is about -// to be passed by the verifier and return if the metrics unit wants to -// collect coverage information on it. -func (mu *Metrics) ShouldGetCoverage() (bool, uint64) { - mu.metricsCollection.recordVerifiedProgram() - if !mu.isKCovSupported { - return false, 0 - } - - if !mu.shouldCollectDetailedInfo() { - return false, 0 - } - return mu.isKCovSupported, mu.KCovSize -} - -func (mu *Metrics) shouldCollectDetailedInfo() bool { - return mu.metricsCollection.getProgramsVerified()%mu.SamplingThreshold == 0 -} // RecordVerificationResults collects metrics from the provided // verification result proto. func (mu *Metrics) RecordVerificationResults(vr *fpb.ValidationResult) { + mu.metricsCollection.recordVerifiedProgram() if vr.GetIsValid() { mu.metricsCollection.recordValidProgram() } @@ -133,7 +111,7 @@ func (mu *Metrics) init() { } // NewMetricsUnit Creates a new Central Metrics Unit. -func NewMetricsUnit(threshold int, kcovSize uint64, vmLinuxPath, sourceFilesPath, metricsServerAddr string, metricsServerPort uint16, cm *CoverageManager) *Metrics { +func NewMetricsUnit(threshold int, vmLinuxPath, sourceFilesPath, metricsServerAddr string, metricsServerPort uint16, cm *CoverageManager) *Metrics { mc := &MetricsCollection{ coverageManager: cm, verifierVerdicts: make(map[string]int), @@ -146,7 +124,6 @@ func NewMetricsUnit(threshold int, kcovSize uint64, vmLinuxPath, sourceFilesPath } mu := &Metrics{ SamplingThreshold: threshold, - KCovSize: kcovSize, metricsCollection: mc, metricsServer: ms, } diff --git a/pkg/units/metrics_unit_test.go b/pkg/units/metrics_unit_test.go index 953ca8f..21d7cb3 100644 --- a/pkg/units/metrics_unit_test.go +++ b/pkg/units/metrics_unit_test.go @@ -40,15 +40,6 @@ func TestMetrics(t *testing.T) { metricsCollection: metricsCollection, } - isKCovSupported, kCovSize := metricsUnit.ShouldGetCoverage() - if !isKCovSupported { - t.Errorf("isKCovSupported = %v, want = true", isKCovSupported) - } - - if kCovSize != expectedKcovSize { - t.Errorf("kCovSize = %d, want = %d", kCovSize, expectedKcovSize) - } - if metricsUnit.metricsCollection.programsVerified != 1 { t.Errorf("metrics unit did not advance the quantity of programs verified") } From 53f72dc8e3d0f803d7b032810655936a31c259c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20L=C3=B3pez=20Jaimez?= Date: Sat, 4 Oct 2025 15:34:34 +0000 Subject: [PATCH 2/4] update the loader tool to use fd arrays --- ebpf_ffi/cbpf.cc | 5 +- ebpf_ffi/ebpf.cc | 5 +- ebpf_ffi/ffi.cc | 169 +++++++++++++++++---------------- ebpf_ffi/ffi.h | 2 +- main.go | 12 +-- pkg/units/control.go | 2 +- pkg/units/metrics_unit.go | 1 - pkg/units/metrics_unit_test.go | 7 +- tools/ffi.go | 1 + tools/loader.cc | 15 +-- 10 files changed, 102 insertions(+), 117 deletions(-) diff --git a/ebpf_ffi/cbpf.cc b/ebpf_ffi/cbpf.cc index 8427cb7..4c51583 100644 --- a/ebpf_ffi/cbpf.cc +++ b/ebpf_ffi/cbpf.cc @@ -19,6 +19,7 @@ #include #include #include + #include "ffi.h" bool load_cbpf_program(void *prog_buff, size_t size, std::string &error, @@ -72,8 +73,8 @@ struct bpf_result ffi_load_cbpf_program(void *prog_buff, size_t size) { bool coverage_enabled = enable_coverage(); bool cbpf_loaded = load_cbpf_program(prog_buff, size, error_message, socks); if (coverage_enabled) { - disable_coverage(); - get_coverage(&vres); + disable_coverage(); + get_coverage(&vres); } if (!cbpf_loaded) { return validation_error(error_message, &vres); diff --git a/ebpf_ffi/ebpf.cc b/ebpf_ffi/ebpf.cc index 156856c..9d48acb 100644 --- a/ebpf_ffi/ebpf.cc +++ b/ebpf_ffi/ebpf.cc @@ -129,11 +129,10 @@ struct bpf_result ffi_load_ebpf_program(void *serialized_proto, size_t size) { ValidationResult vres = load_ebpf_program(program, error_message); vres.set_did_collect_coverage(false); if (coverage_enabled) { - get_coverage(&vres); - disable_coverage(); + get_coverage(&vres); + disable_coverage(); } - vres.set_is_valid(true); if (vres.program_fd() < 0) { // Return why we failed to load the program. diff --git a/ebpf_ffi/ffi.cc b/ebpf_ffi/ffi.cc index c785b02..d46e28e 100644 --- a/ebpf_ffi/ffi.cc +++ b/ebpf_ffi/ffi.cc @@ -44,103 +44,106 @@ using ebpf_fuzzer::ExecutionResult; using ebpf_fuzzer::MapElements; using ebpf_fuzzer::ValidationResult; -struct coverage_data* kCoverageData = nullptr; +struct coverage_data *kCoverageData = nullptr; // All the functions in this extern are FFIs intended to be invoked from go. extern "C" { - bpf_result serialize_proto(const google::protobuf::Message &proto) { - std::string proto_encoded; - absl::Base64Escape(proto.SerializeAsString(), &proto_encoded); +bpf_result serialize_proto(const google::protobuf::Message &proto) { + std::string proto_encoded; + absl::Base64Escape(proto.SerializeAsString(), &proto_encoded); - // The memory for this string will be freed by the Go program. - char *serialized_proto = - reinterpret_cast(malloc(proto_encoded.size() + 1)); - strncpy(serialized_proto, proto_encoded.c_str(), proto_encoded.size()); + // The memory for this string will be freed by the Go program. + char *serialized_proto = + reinterpret_cast(malloc(proto_encoded.size() + 1)); + strncpy(serialized_proto, proto_encoded.c_str(), proto_encoded.size()); - struct bpf_result res; - res.serialized_proto = serialized_proto; - res.size = proto_encoded.size(); - return res; - } + struct bpf_result res; + res.serialized_proto = serialized_proto; + res.size = proto_encoded.size(); + return res; +} - void get_coverage(ValidationResult *vres) { - if (kCoverageData == nullptr || kCoverageData->fd == -1) return; - uint64_t trace_size = kCoverageData->coverage_buffer[0]; - - auto *coverage_addresses = vres->mutable_coverage_address(); - absl::flat_hash_set seen_address; - for (uint64_t i = 0; i < trace_size; i++) { - uint64_t addr = kCoverageData->coverage_buffer[i + 1]; - if (seen_address.find(addr) == seen_address.end()) { - coverage_addresses->Add(addr); - seen_address.insert(addr); - } - } - vres->set_did_collect_coverage(true); - vres->set_coverage_size(trace_size); - return; +void get_coverage(ValidationResult *vres) { + if (kCoverageData == nullptr || kCoverageData->fd == -1) return; + uint64_t trace_size = kCoverageData->coverage_buffer[0]; + + auto *coverage_addresses = vres->mutable_coverage_address(); + absl::flat_hash_set seen_address; + for (uint64_t i = 0; i < trace_size; i++) { + uint64_t addr = kCoverageData->coverage_buffer[i + 1]; + if (seen_address.find(addr) == seen_address.end()) { + coverage_addresses->Add(addr); + seen_address.insert(addr); } + } - bool enable_coverage() { - if (!kCoverageData || kCoverageData->fd == -1) return false; - return ioctl(kCoverageData->fd, KCOV_ENABLE, KCOV_TRACE_PC) == 0; - } + // reset kcov buffer. + memset(kCoverageData->coverage_buffer, 0, KCOV_SIZE * sizeof(uint64_t)); + vres->set_did_collect_coverage(true); + vres->set_coverage_size(trace_size); + return; +} - void disable_coverage() { - if (kCoverageData == nullptr || kCoverageData->fd == -1) return; - (void)ioctl(kCoverageData->fd, KCOV_DISABLE, 0); - } +bool enable_coverage() { + if (!kCoverageData || kCoverageData->fd == -1) return false; + return ioctl(kCoverageData->fd, KCOV_ENABLE, KCOV_TRACE_PC) == 0; +} - int ffi_setup_coverage() { - if (!kCoverageData) { - kCoverageData = (struct coverage_data*)malloc(sizeof(struct coverage_data)); - memset(kCoverageData, 0, sizeof(struct coverage_data)); - } - - int fd = open("/sys/kernel/debug/kcov", O_RDWR); - kCoverageData->fd = fd; - if (fd == -1) return -1; - /* Setup trace mode and trace size. */ - if (ioctl(fd, KCOV_INIT_TRACE, KCOV_SIZE)) return -1; - /* Mmap buffer shared between kernel- and user-space. */ - uint64_t *cover = - (uint64_t *)mmap(nullptr, KCOV_SIZE * sizeof(uint64_t), - PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if ((void *)cover == MAP_FAILED) return -1; - memset(cover, 0, KCOV_SIZE * sizeof(uint64_t)); - - kCoverageData->fd = fd; - kCoverageData->coverage_buffer = cover; - return 0; - } +void disable_coverage() { + if (kCoverageData == nullptr || kCoverageData->fd == -1) return; + (void)ioctl(kCoverageData->fd, KCOV_DISABLE, 0); +} - int ffi_cleanup_coverage() { - if (!kCoverageData) return 0; +int ffi_setup_coverage() { + if (!kCoverageData) { + kCoverageData = + (struct coverage_data *)malloc(sizeof(struct coverage_data)); + memset(kCoverageData, 0, sizeof(struct coverage_data)); + } + + int fd = open("/sys/kernel/debug/kcov", O_RDWR); + kCoverageData->fd = fd; + if (fd == -1) return -1; + /* Setup trace mode and trace size. */ + if (ioctl(fd, KCOV_INIT_TRACE, KCOV_SIZE)) return -1; + /* Mmap buffer shared between kernel- and user-space. */ + uint64_t *cover = (uint64_t *)mmap(nullptr, KCOV_SIZE * sizeof(uint64_t), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if ((void *)cover == MAP_FAILED) return -1; + memset(cover, 0, KCOV_SIZE * sizeof(uint64_t)); + + kCoverageData->fd = fd; + kCoverageData->coverage_buffer = cover; + return 0; +} - close(kCoverageData->fd); - munmap(kCoverageData->coverage_buffer, KCOV_SIZE * sizeof(uint64_t)); - free(kCoverageData); - kCoverageData = nullptr; - return 0; - } +int ffi_cleanup_coverage() { + if (!kCoverageData) return 0; - bool execute_error(std::string &error_message, const char *strerr, - int *sockets) { - if (sockets != nullptr) { - close(sockets[0]); - close(sockets[1]); - } - error_message = strerr; - return false; - } + close(kCoverageData->fd); + munmap(kCoverageData->coverage_buffer, KCOV_SIZE * sizeof(uint64_t)); + free(kCoverageData); + kCoverageData = nullptr; + return 0; +} - struct bpf_result return_error(std::string error_message, - ExecutionResult *result) { - result->set_did_succeed(false); - result->set_error_message(error_message); - return serialize_proto(*result); - } +bool execute_error(std::string &error_message, const char *strerr, + int *sockets) { + if (sockets != nullptr) { + close(sockets[0]); + close(sockets[1]); + } + error_message = strerr; + return false; +} + +struct bpf_result return_error(std::string error_message, + ExecutionResult *result) { + result->set_did_succeed(false); + result->set_error_message(error_message); + return serialize_proto(*result); +} - void ffi_close_fd(int prog_fd) { close(prog_fd); } +void ffi_close_fd(int prog_fd) { close(prog_fd); } } diff --git a/ebpf_ffi/ffi.h b/ebpf_ffi/ffi.h index d08d5ae..0e7ac52 100644 --- a/ebpf_ffi/ffi.h +++ b/ebpf_ffi/ffi.h @@ -97,7 +97,7 @@ struct coverage_data { uint64_t *coverage_buffer; }; -extern struct coverage_data* kCoverageData; +extern struct coverage_data *kCoverageData; } #endif // EBPF_FUZZER_EBPF_FFI_FFI_H_ diff --git a/main.go b/main.go index 15d42c1..738e57e 100644 --- a/main.go +++ b/main.go @@ -27,12 +27,12 @@ import ( // Flags that the binary can accept. var ( - metricsThreshold = flag.Int("metrics_threshold", 200, "Collect detailed metrics (coverage) every `metrics_threshold` validated programs") - strategyName = flag.String("strategy", "playground", "Strategy to use for fuzzing") - vmLinuxPath = flag.String("vmlinux_path", "/root/vmlinux", "Path to the linux image that will be passed to addr2line to get coverage info") - sourceFilesPath = flag.String("src_path", "/root/sourceFiles", "The fuzzer will look for source files to visualize the coverage at this path") - metricsServerAddr = flag.String("metrics_server_addr", "0.0.0.0", "Address that the metrics server will listen to at") - metricsServerPort = flag.Uint("metrics_server_port", 8080, "Port that the metrics server will listen to at") + metricsThreshold = flag.Int("metrics_threshold", 200, "Collect detailed metrics (coverage) every `metrics_threshold` validated programs") + strategyName = flag.String("strategy", "playground", "Strategy to use for fuzzing") + vmLinuxPath = flag.String("vmlinux_path", "/root/vmlinux", "Path to the linux image that will be passed to addr2line to get coverage info") + sourceFilesPath = flag.String("src_path", "/root/sourceFiles", "The fuzzer will look for source files to visualize the coverage at this path") + metricsServerAddr = flag.String("metrics_server_addr", "0.0.0.0", "Address that the metrics server will listen to at") + metricsServerPort = flag.Uint("metrics_server_port", 8080, "Port that the metrics server will listen to at") ) var ( diff --git a/pkg/units/control.go b/pkg/units/control.go index 0ed4a89..0618142 100644 --- a/pkg/units/control.go +++ b/pkg/units/control.go @@ -87,7 +87,7 @@ func (cu *Control) IsReady() bool { // RunFuzzer kickstars the fuzzer in the mode that was specified at Init time. func (cu *Control) RunFuzzer() error { cu.ffi.InitKcov() - defer func(){ + defer func() { cu.ffi.CleanupKcov() }() for !cu.strat.IsFuzzingDone() { diff --git a/pkg/units/metrics_unit.go b/pkg/units/metrics_unit.go index 3777438..e5dd9c9 100644 --- a/pkg/units/metrics_unit.go +++ b/pkg/units/metrics_unit.go @@ -90,7 +90,6 @@ func (mu *Metrics) validationResultProcessingRoutine() { } } - // RecordVerificationResults collects metrics from the provided // verification result proto. func (mu *Metrics) RecordVerificationResults(vr *fpb.ValidationResult) { diff --git a/pkg/units/metrics_unit_test.go b/pkg/units/metrics_unit_test.go index 21d7cb3..e02e05a 100644 --- a/pkg/units/metrics_unit_test.go +++ b/pkg/units/metrics_unit_test.go @@ -21,7 +21,6 @@ import ( ) func TestMetrics(t *testing.T) { - expectedKcovSize := uint64(42) cm := &CoverageManager{ coverageCache: make(map[uint64]string), coverageInfoMap: make(map[string][]int), @@ -35,20 +34,16 @@ func TestMetrics(t *testing.T) { metricsUnit := Metrics{ SamplingThreshold: 1, - KCovSize: expectedKcovSize, isKCovSupported: true, metricsCollection: metricsCollection, } - if metricsUnit.metricsCollection.programsVerified != 1 { - t.Errorf("metrics unit did not advance the quantity of programs verified") - } - vr := &fpb.ValidationResult{ IsValid: true, DidCollectCoverage: true, } metricsUnit.RecordVerificationResults(vr) + if metricsUnit.metricsCollection.validPrograms != 1 { t.Errorf("metrics unit did not advance the quantity of valid programs") } diff --git a/tools/ffi.go b/tools/ffi.go index 83758a3..3f5320f 100644 --- a/tools/ffi.go +++ b/tools/ffi.go @@ -37,6 +37,7 @@ func EncodeEBPF(serializedProgram unsafe.Pointer, serializedProgramSize C.int, Program: encodedProg, Function: encodedfunc, Btf: program.Btf, + Maps: program.Maps, } // Then do magic to return it to C++ serializedProto, err := proto.Marshal(result) diff --git a/tools/loader.cc b/tools/loader.cc index c3ff9bd..f670eff 100644 --- a/tools/loader.cc +++ b/tools/loader.cc @@ -32,9 +32,6 @@ int main(int argc, char **argv) { return -1; } - const int map_size = 2; - int map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(uint32_t), - sizeof(uint64_t), map_size); std::string verifier_log, error_message; std::string serialized_proto_string( reinterpret_cast(serialized_proto), size); @@ -58,17 +55,7 @@ int main(int argc, char **argv) { return -1; } - std::vector map_elements; - if (!get_map_elements(map_fd, map_size, &map_elements, error_message)) { - std::cerr << "Could not get map elements: " << error_message << std::endl; - return -1; - } - - std::cout << "map elements: " << std::endl; - for (auto element : map_elements) { - std::cout << "element: " << element << std::endl; - } - + ffi_clean_fd_array(vres.fd_array_addr(), program.maps().size()); free(serialized_proto); return 0; } From da77deb74686701328ed363e0a77b4dd8d55893d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20L=C3=B3pez=20Jaimez?= Date: Tue, 21 Oct 2025 11:42:48 +0000 Subject: [PATCH 3/4] implement the minimizer of PoCs --- pkg/ebpf/instruction_generators.go | 10 ++ pkg/ebpf/poc_generator.go | 159 ++++++++++++++++++++++++++++- pkg/units/control.go | 1 - pkg/units/ffi.go | 4 +- tools/BUILD | 18 ++++ tools/minimizer.go | 74 ++++++++++++++ 6 files changed, 261 insertions(+), 5 deletions(-) create mode 100644 tools/minimizer.go diff --git a/pkg/ebpf/instruction_generators.go b/pkg/ebpf/instruction_generators.go index fcd4ae2..8a7a4b0 100644 --- a/pkg/ebpf/instruction_generators.go +++ b/pkg/ebpf/instruction_generators.go @@ -17,6 +17,7 @@ package ebpf import ( "buzzer/pkg/rand" pb "buzzer/proto/ebpf_go_proto" + protobuf "github.com/golang/protobuf/proto" ) // GenerateRandomAluInstruction provides a random ALU operation with either @@ -234,3 +235,12 @@ func generateRegAluInstruction(op pb.AluOperationCode, insClass pb.InsClass, dst return newAluInstruction(op, insClass, dstReg, srcReg) } + +func DuplicateProgram(prog []*pb.Instruction) []*pb.Instruction { + ret := []*pb.Instruction{} + for _, ins := range prog { + insCp := protobuf.Clone(ins).(*pb.Instruction) + ret = append(ret, insCp) + } + return ret +} diff --git a/pkg/ebpf/poc_generator.go b/pkg/ebpf/poc_generator.go index 0bf7443..6fa98ca 100644 --- a/pkg/ebpf/poc_generator.go +++ b/pkg/ebpf/poc_generator.go @@ -19,12 +19,11 @@ import ( "errors" "fmt" jsonpb "github.com/golang/protobuf/jsonpb" + protobuf "github.com/golang/protobuf/proto" "os" ) -// GeneratePoc generates a c program that can be used to reproduce fuzzer -// test cases. -func GeneratePoc(program *pb.Program) error { +func writeJsonProgram(program *pb.Program) error { m := &jsonpb.Marshaler{ OrigName: true, EnumsAsInts: false, @@ -43,5 +42,159 @@ func GeneratePoc(program *pb.Program) error { fmt.Printf("Writing eBPF PoC %q.\n", f.Name()) _, err = f.Write([]byte(textpbData)) return errors.Join(err, f.Close()) +} + +func copyProgram(program *pb.Program) *pb.Program { + return protobuf.Clone(program).(*pb.Program) +} + +func Minimizer(program *pb.Program, bugCheckFunction func(*pb.Program) bool, footerSize int) (*pb.Program, error) { + fmt.Println("Running minimizer...") + minimalProgram := copyProgram(program) + minimalProgramInstructions := DuplicateProgram(program.Functions[0].Instructions) + if footerSize > len(minimalProgramInstructions) { + return nil, fmt.Errorf("footerSize > len(program) (%d vs %d)", footerSize, len(minimalProgramInstructions)) + } + + minimalProgram.Functions[0].Instructions = minimalProgramInstructions + noop := Jmp(0) + + phase1 := func() bool { + // First step: change all instructions not contributing to the bug + // to be noops. + changed := true + fmt.Println("Step 1: noop'ing instructions") + roundCount := 1 + nooped := 0 + for changed { + changed = false + for i := 0; i < len(minimalProgramInstructions)-footerSize; i++ { + fmt.Printf("\tattempting to noop instruction: %d (of %d), nooped: %d \r", i, len(minimalProgramInstructions), nooped) + prev := minimalProgramInstructions[i] + + // Don't process any previous noops + if protobuf.Equal(prev, noop) { + continue + } + + // Replace instruction with a noop + minimalProgramInstructions[i] = noop + if bugCheckFunction(minimalProgram) { + changed = true + nooped += 1 + } else { + minimalProgramInstructions[i] = prev + } + } + fmt.Printf("\n\tround %d finished\n", roundCount) + roundCount += 1 + } + return nooped != 0 + } + + phase2 := func() bool { + changesDone := false + // Second step: reduce the jumps to a minimum offset. + fmt.Println("--------------------------------------") + fmt.Println("Step 2: minimizing jumps") + for i := 0; i < len(minimalProgramInstructions); i++ { + insn := minimalProgramInstructions[i] + switch insn.Opcode.(type) { + case *pb.Instruction_JmpOpcode: + if insn.Offset == 0 { + continue + } + previousOffset := insn.Offset + insn.Offset -= 1 + fmt.Printf("\tminimizing offset of instruction %d (initial: %d)\n", i, previousOffset) + for bugCheckFunction(minimalProgram) && insn.Offset != 0 { + changesDone = true + previousOffset = insn.Offset + insn.Offset -= 1 + fmt.Printf("\t\treducing offset to: %d \r", insn.Offset) + } + fmt.Printf("\t\tfound minimal offset: %d \n", previousOffset) + insn.Offset = previousOffset + default: + continue + } + } + return changesDone + } + phase3 := func() (bool, []*pb.Instruction) { + fmt.Println("--------------------------------------") + fmt.Println("Step 3: removing noop instructions") + removed := 0 + minimalModified := []*pb.Instruction{} + for i := 0; i < len(minimalProgramInstructions); i++ { + insn := minimalProgramInstructions[i] + fmt.Printf("\tattempting to remove instruction: %d, removed: %d \r", i, removed) + if !protobuf.Equal(insn, noop) { + minimalModified = append(minimalModified, insn) + continue + } + + candidateProgram := []*pb.Instruction{} + if i != (len(minimalProgramInstructions) - 1) { + candidateProgram = append(minimalModified, minimalProgramInstructions[i+1:]...) + } else { + candidateProgram = minimalModified + } + + // If bug is no longer present then the instruction cannot be removed + if !bugCheckFunction(&pb.Program{ + Functions: []*pb.Functions{ + { + Instructions: candidateProgram, + }, + }, + Maps: program.Maps, + }) { + minimalModified = append(minimalModified, insn) + } else { + // if the bug is present without the instruction then it can + // be removed from the program. + removed += 1 + } + } + fmt.Println() + return removed != 0, minimalModified + } + + changesDone := true + iteration := 0 + for changesDone { + fmt.Printf("> iteration: %d of minimizer\n", iteration) + changesDone = false + + // Noop instructions. + changes := phase1() + changesDone = changesDone || changes + + // Reduce jumps. + changes = phase2() + changesDone = changesDone || changes + + // Remove noops. + changes, minInstructions := phase3() + + changesDone = changesDone || changes + minimalProgramInstructions = minInstructions + minimalProgram.Functions[0].Instructions = minimalProgramInstructions + + iteration += 1 + } + return minimalProgram, nil + +} + +// GeneratePoc generates a c program that can be used to reproduce fuzzer +// test cases. +func GeneratePoc(program *pb.Program, bugCheckFunction func(*pb.Program) bool, footerSize int) error { + minimizedProgram, err := Minimizer(program, bugCheckFunction, footerSize) + if err != nil { + return err + } + return writeJsonProgram(minimizedProgram) } diff --git a/pkg/units/control.go b/pkg/units/control.go index 0618142..3e29490 100644 --- a/pkg/units/control.go +++ b/pkg/units/control.go @@ -178,7 +178,6 @@ func (cu *Control) runEbpf(prog *epb.Program) error { ok := cu.strat.OnExecuteDone(cu.ffi, exRes) if !ok { fmt.Println("Program produced unexpected results") - ebpf.GeneratePoc(prog) } return nil } diff --git a/pkg/units/ffi.go b/pkg/units/ffi.go index e8750a4..45d5045 100644 --- a/pkg/units/ffi.go +++ b/pkg/units/ffi.go @@ -165,7 +165,9 @@ func (e *FFI) ValidateEbpfProgram(encodedProgram *fpb.EncodedProgram) (*fpb.Vali if err != nil { return nil, err } - e.MetricsUnit.RecordVerificationResults(res) + if e.MetricsUnit != nil { + e.MetricsUnit.RecordVerificationResults(res) + } return res, nil } diff --git a/tools/BUILD b/tools/BUILD index 0001194..ec702ce 100644 --- a/tools/BUILD +++ b/tools/BUILD @@ -20,6 +20,24 @@ package( ], ) +go_binary( + name = "minimizer", + srcs = ["minimizer.go"], + gc_linkopts = [ + "-linkmode=external", + "-extldflags=-static", + ], + static = "on", + deps = [ + "//pkg/ebpf", + "//pkg/units", + "//proto:ebpf_go_proto", + "//proto:ffi_go_proto", + "@com_github_golang_protobuf//jsonpb", + "@com_github_golang_protobuf//proto", + ], +) + go_binary( name = "ffi", srcs = ["ffi.go"], diff --git a/tools/minimizer.go b/tools/minimizer.go new file mode 100644 index 0000000..383b167 --- /dev/null +++ b/tools/minimizer.go @@ -0,0 +1,74 @@ +package main + +import ( + "buzzer/pkg/ebpf/ebpf" + "buzzer/pkg/units/units" + epb "buzzer/proto/ebpf_go_proto" + fpb "buzzer/proto/ffi_go_proto" + "flag" + "fmt" + jsonpb "github.com/golang/protobuf/jsonpb" + "os" + "strings" +) + +var ( + samplePath = flag.String("sample", "", "JSON sample of the ebpf program to minimize") +) + +func main() { + flag.Parse() + fmt.Println("hello world!") + + ffi := &units.FFI{ + MetricsUnit: nil, + } + + encodedProto, err := os.ReadFile(*samplePath) + + if err != nil { + fmt.Printf("error reading file: %v\n") + return + } + + program := &epb.Program{} + err = jsonpb.UnmarshalString(string(encodedProto), program) + if err != nil { + fmt.Println(err) + return + } + err = ebpf.GeneratePoc(program, func(prog *epb.Program) bool { + encodedProg, encodedFuncInfo, err := ebpf.EncodeInstructions(prog) + + if err != nil { + fmt.Printf("Encoding error: %v\n", err) + return false + } + + encodedProgram := &fpb.EncodedProgram{ + Program: encodedProg, + Btf: prog.Btf, + Function: encodedFuncInfo, + Maps: prog.Maps, + } + validationResult, err := ffi.ValidateEbpfProgram(encodedProgram) + defer func() { + ffi.CleanFdArray(validationResult.FdArrayAddr, len(prog.Maps)) + }() + ebpf.LatestLogMinimizer = validationResult.VerifierLog + + if !validationResult.IsValid { + return false + } + + defer func() { + ffi.CloseFD(int(validationResult.ProgramFd)) + }() + + return strings.Contains(validationResult.VerifierLog, "verifier bug") + + }) + if err != nil { + fmt.Printf("%v", err) + } +} From d71d7a698a973324a3badaec210358dc89e1df96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20L=C3=B3pez=20Jaimez?= Date: Tue, 21 Oct 2025 13:30:08 +0000 Subject: [PATCH 4/4] remove the minimizer tool, it was meant for development purposes only --- tools/BUILD | 18 ----------- tools/minimizer.go | 74 ---------------------------------------------- 2 files changed, 92 deletions(-) delete mode 100644 tools/minimizer.go diff --git a/tools/BUILD b/tools/BUILD index ec702ce..0001194 100644 --- a/tools/BUILD +++ b/tools/BUILD @@ -20,24 +20,6 @@ package( ], ) -go_binary( - name = "minimizer", - srcs = ["minimizer.go"], - gc_linkopts = [ - "-linkmode=external", - "-extldflags=-static", - ], - static = "on", - deps = [ - "//pkg/ebpf", - "//pkg/units", - "//proto:ebpf_go_proto", - "//proto:ffi_go_proto", - "@com_github_golang_protobuf//jsonpb", - "@com_github_golang_protobuf//proto", - ], -) - go_binary( name = "ffi", srcs = ["ffi.go"], diff --git a/tools/minimizer.go b/tools/minimizer.go deleted file mode 100644 index 383b167..0000000 --- a/tools/minimizer.go +++ /dev/null @@ -1,74 +0,0 @@ -package main - -import ( - "buzzer/pkg/ebpf/ebpf" - "buzzer/pkg/units/units" - epb "buzzer/proto/ebpf_go_proto" - fpb "buzzer/proto/ffi_go_proto" - "flag" - "fmt" - jsonpb "github.com/golang/protobuf/jsonpb" - "os" - "strings" -) - -var ( - samplePath = flag.String("sample", "", "JSON sample of the ebpf program to minimize") -) - -func main() { - flag.Parse() - fmt.Println("hello world!") - - ffi := &units.FFI{ - MetricsUnit: nil, - } - - encodedProto, err := os.ReadFile(*samplePath) - - if err != nil { - fmt.Printf("error reading file: %v\n") - return - } - - program := &epb.Program{} - err = jsonpb.UnmarshalString(string(encodedProto), program) - if err != nil { - fmt.Println(err) - return - } - err = ebpf.GeneratePoc(program, func(prog *epb.Program) bool { - encodedProg, encodedFuncInfo, err := ebpf.EncodeInstructions(prog) - - if err != nil { - fmt.Printf("Encoding error: %v\n", err) - return false - } - - encodedProgram := &fpb.EncodedProgram{ - Program: encodedProg, - Btf: prog.Btf, - Function: encodedFuncInfo, - Maps: prog.Maps, - } - validationResult, err := ffi.ValidateEbpfProgram(encodedProgram) - defer func() { - ffi.CleanFdArray(validationResult.FdArrayAddr, len(prog.Maps)) - }() - ebpf.LatestLogMinimizer = validationResult.VerifierLog - - if !validationResult.IsValid { - return false - } - - defer func() { - ffi.CloseFD(int(validationResult.ProgramFd)) - }() - - return strings.Contains(validationResult.VerifierLog, "verifier bug") - - }) - if err != nil { - fmt.Printf("%v", err) - } -}