From 4eb901927957f3fb59e674d3ba162562b8b9a522 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20L=C3=B3pez=20Jaimez?= Date: Fri, 3 Oct 2025 16:09:48 +0000 Subject: [PATCH 1/3] update the coverage based strategy to use fd arrays for maps --- MODULE.bazel.lock | 2 +- ebpf_ffi/ebpf.cc | 15 ++++++++++- ebpf_ffi/ebpf.h | 5 ++++ pkg/strategies/coverage_based.go | 44 +++++++++++++++++--------------- pkg/units/control.go | 6 ++++- pkg/units/ffi.go | 7 +++++ proto/ffi.proto | 1 + 7 files changed, 57 insertions(+), 23 deletions(-) diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index c6d13b6..6d3981c 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -571,7 +571,7 @@ }, "@@rules_go+//go:extensions.bzl%go_sdk": { "os:linux,arch:amd64": { - "bzlTransitiveDigest": "SxAVT7Q4rpPYaEceNwyKt2uMO81h49UZbOS6EEiugKA=", + "bzlTransitiveDigest": "scG7qz3VwrcX0hTvzL5m9BnJF+OOnt1LULgya77mEqo=", "usagesDigest": "9C0KgnNPql7IwwveR5Oa8Jy3lqq523h2IOh9UMhUXKg=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, diff --git a/ebpf_ffi/ebpf.cc b/ebpf_ffi/ebpf.cc index 0a9c3cb..324189f 100644 --- a/ebpf_ffi/ebpf.cc +++ b/ebpf_ffi/ebpf.cc @@ -90,7 +90,7 @@ ValidationResult load_ebpf_program(EncodedProgram program, std::string &error) { attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER; attr.insns = (uint64_t)insn; attr.insn_cnt = ((program.program().length()) / (sizeof(struct bpf_insn))); - attr.license = (uint64_t) "GPL"; + attr.license = (uint64_t)"GPL"; attr.log_size = ebpf_ffi::kLogBuffSize; attr.log_buf = (uint64_t)log_buf; attr.log_level = 2; @@ -212,6 +212,19 @@ struct bpf_result ffi_get_map_elements(int map_fd, uint64_t map_size) { return serialize_proto(res); } +struct bpf_result ffi_get_map_elements_fd_array(uint64_t fd_array_addr, + uint32_t idx, + uint64_t map_size) { + MapElements res; + std::vector elements; + std::string error_message; + + int *fd_array = reinterpret_cast(fd_array_addr); + int map_fd = fd_array[idx]; + + return ffi_get_map_elements(map_fd, map_size); +} + bool execute_ebpf_program(int prog_fd, uint8_t *input, int input_length, std::string &error_message) { int socks[2] = {}; diff --git a/ebpf_ffi/ebpf.h b/ebpf_ffi/ebpf.h index 8a6c409..57fa3b2 100644 --- a/ebpf_ffi/ebpf.h +++ b/ebpf_ffi/ebpf.h @@ -54,6 +54,11 @@ int ffi_create_bpf_map(size_t size); // MapElements. struct bpf_result ffi_get_map_elements(int map_fd, uint64_t map_size); +// Retrieves all the map elements out of a map whose fd is stored in a fd array. +struct bpf_result ffi_get_map_elements_fd_array(uint64_t fd_array_addr, + uint32_t idx, + uint64_t map_size); + bool execute_ebpf_program(int prog_fd, uint8_t *input, int input_length, std::string &error_message); diff --git a/pkg/strategies/coverage_based.go b/pkg/strategies/coverage_based.go index b4fa03d..8f7bbec 100644 --- a/pkg/strategies/coverage_based.go +++ b/pkg/strategies/coverage_based.go @@ -33,7 +33,7 @@ func NewCoverageBasedStrategy() *CoverageBased { // value as well as all stack locations from -8 to -512. defaultProg, _ := InstructionSequence( // Need to patch the fd on every run of prog generation. - LdMapByFd(R1, 0), + LdMapByIdx(R1, 0), StW(R10, 0, -4), Mov64(R2, R10), Add64(R2, -4), @@ -61,8 +61,13 @@ func NewCoverageBasedStrategy() *CoverageBased { fingerprintHashTable: make(map[uint64]bool), programCount: 0, validProgramCount: 0, - mapFd: -1, - defaultProg: defaultProg, + bpfMap: &epb.EbpfMap{ + Type: epb.BpfMapType_ARRAY, + KeySize: 4, + ValueSize: 8, + MaxEntries: 2, + }, + defaultProg: defaultProg, } } @@ -78,17 +83,17 @@ type CoverageBased struct { programCount int validProgramCount int lastProgram []*epb.Instruction - mapFd int + bpfMap *epb.EbpfMap defaultProg []*epb.Instruction } -func mapPtrArithmeticFooter(randomReg epb.Reg, mapFd int) ([]*epb.Instruction, error) { +func mapPtrArithmeticFooter(randomReg epb.Reg) ([]*epb.Instruction, error) { return InstructionSequence( // Select a random register and store its value in R8. Mov64(R8, randomReg), - // Load a fd to the map. - LdMapByFd(R9, mapFd), + // Load a fd to the map. Should be stored in a fd array. + LdMapByIdx(R9, 0), StW(R10, 0, -4), Mov64(R2, R10), @@ -215,18 +220,7 @@ func (cv *CoverageBased) GenerateProgram(ffi *units.FFI) (*pb.Program, error) { return nil, err } - // For the footer, write a control and test value to a map, control will - // not do ptr arithmetic, test will attempt to do some and see if the - // verifier thinks its safe. We will validate this assumption in onExecuteDone. - ffi.CloseFD(cv.mapFd) - cv.mapFd = ffi.CreateMapArray(1) - if cv.mapFd < 0 { - return nil, mapCreationFailed - } - - mutatedProgram[0].Immediate = int32(cv.mapFd) - - footer, err := mapPtrArithmeticFooter(RandomRegister(), cv.mapFd) + footer, err := mapPtrArithmeticFooter(RandomRegister()) if err != nil { return nil, err } @@ -236,6 +230,7 @@ func (cv *CoverageBased) GenerateProgram(ffi *units.FFI) (*pb.Program, error) { Functions: []*epb.Functions{ {Instructions: append(mutatedProgram, footer...)}, }, + Maps: []*epb.EbpfMap{cv.bpfMap}, }, }, } @@ -302,7 +297,16 @@ func (cv *CoverageBased) OnVerifyDone(ffi *units.FFI, verificationResult *fpb.Va // OnExecuteDone should validate if the program behaved like the // verifier expected, if that was not the case it should return false. func (cv *CoverageBased) OnExecuteDone(ffi *units.FFI, executionResult *fpb.ExecutionResult) bool { - mapEl, _ := ffi.GetMapElements(cv.mapFd, 1) + if executionResult.FdArray == 0 { + fmt.Println("Error: invalid fd array address (null)") + return false + } + mapEl, _ := ffi.GetMapElementsFdArray(executionResult.FdArray, 0, 1) + if len(mapEl.Elements) == 0 { + fmt.Println("error reading map elements: ") + fmt.Println(mapEl.ErrorMessage) + return false + } valid := mapEl.Elements[0] == 0xCAFE cv.isFinished = !valid return valid diff --git a/pkg/units/control.go b/pkg/units/control.go index 580cbbf..32e42dd 100644 --- a/pkg/units/control.go +++ b/pkg/units/control.go @@ -158,7 +158,9 @@ func (cu *Control) runEbpf(prog *epb.Program) error { } exRes, err := cu.ffi.RunEbpfProgram(exReq) - cu.ffi.CloseFD(int(validationResult.ProgramFd)) + defer func() { + cu.ffi.CloseFD(int(validationResult.ProgramFd)) + }() if err != nil { fmt.Printf("RunProgram error: %v\n", err) if !cu.strat.OnError(err) { @@ -167,6 +169,8 @@ func (cu *Control) runEbpf(prog *epb.Program) error { return nil } + exRes.FdArray = validationResult.FdArrayAddr + ok := cu.strat.OnExecuteDone(cu.ffi, exRes) if !ok { fmt.Println("Program produced unexpected results") diff --git a/pkg/units/ffi.go b/pkg/units/ffi.go index 5cb4e99..f6a78ca 100644 --- a/pkg/units/ffi.go +++ b/pkg/units/ffi.go @@ -26,6 +26,7 @@ package units //struct bpf_result ffi_load_ebpf_program(void* serialized_proto, size_t size, int coverage_enabled, unsigned long coverage_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); //int ffi_create_bpf_map(size_t size); //void ffi_close_fd(int fd); //int ffi_update_map_element(int map_fd, int key, uint64_t value); @@ -127,6 +128,12 @@ func (e *FFI) GetMapElements(fd int, mapSize uint64) (*fpb.MapElements, error) { return mapElementsProtoFromStruct(&res) } +// GetMapElements fetches the map elements of the given fd_array position. +func (e *FFI) GetMapElementsFdArray(fd_array uint64, idx uint32, mapSize uint32) (*fpb.MapElements, error) { + res := C.ffi_get_map_elements_fd_array(C.ulong(fd_array), C.uint(idx), C.ulong(mapSize)) + return mapElementsProtoFromStruct(&res) +} + // SetMapElement sets the elemnt specified by `key` to `value` in the map // described by `fd` func (e *FFI) SetMapElement(fd int, key uint32, value uint64) int { diff --git a/proto/ffi.proto b/proto/ffi.proto index 4e7bbe1..dc64f2c 100644 --- a/proto/ffi.proto +++ b/proto/ffi.proto @@ -42,6 +42,7 @@ message ExecutionResult { bool did_succeed = 1; string error_message = 2; bytes output_data = 3; + uint64 fd_array = 4; } // Result from get_map_elements call, retrieves all the elements in a bpf map. From 392dbe0c08ec415b096ab5b56cb9ddcb08b22ad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20L=C3=B3pez=20Jaimez?= Date: Fri, 3 Oct 2025 16:13:13 +0000 Subject: [PATCH 2/3] use the right clang format --- ebpf_ffi/ebpf.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ebpf_ffi/ebpf.cc b/ebpf_ffi/ebpf.cc index 324189f..e21df7c 100644 --- a/ebpf_ffi/ebpf.cc +++ b/ebpf_ffi/ebpf.cc @@ -90,7 +90,7 @@ ValidationResult load_ebpf_program(EncodedProgram program, std::string &error) { attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER; attr.insns = (uint64_t)insn; attr.insn_cnt = ((program.program().length()) / (sizeof(struct bpf_insn))); - attr.license = (uint64_t)"GPL"; + attr.license = (uint64_t) "GPL"; attr.log_size = ebpf_ffi::kLogBuffSize; attr.log_buf = (uint64_t)log_buf; attr.log_level = 2; From e71d95b0e6c5a3d10e40c4e972ea60f83039abe8 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:53 +0000 Subject: [PATCH 3/3] fix bug on the closing of the ebpf map function --- ebpf_ffi/ebpf.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ebpf_ffi/ebpf.cc b/ebpf_ffi/ebpf.cc index e21df7c..7651d10 100644 --- a/ebpf_ffi/ebpf.cc +++ b/ebpf_ffi/ebpf.cc @@ -281,7 +281,7 @@ struct bpf_result ffi_execute_ebpf_program(void *serialized_proto, void ffi_clean_fd_array(unsigned long long int addr, int size) { int *fd_array = reinterpret_cast(addr); for (int i = 0; i < size; i++) { - close(fd_array[size]); + close(fd_array[i]); } free(fd_array); }