diff options
author | Catena cyber <35799796+catenacyber@users.noreply.github.com> | 2021-09-27 17:11:36 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-27 11:11:36 -0400 |
commit | 5c386a4858611e2e4b83dadcfc03e6701aafba6f (patch) | |
tree | 31fce885e48ed8fd5da522d4da86e2f55274d8da /infra/base-images | |
parent | 13135d51aa20fd488b1f07abb818c0a34995bfe2 (diff) |
Fix for rust and swift coverages (#6517)
* coverage: introduces llvm-cov-rel
Cf https://github.com/google/oss-fuzz/issues/6268
Latest clang-14 and clang-13 used by rust or swift have a slightly
different profraw file format
llvm-cov-rel is tool that will update the profraw file produced
by clang-13 to one readable by clang-14 llvm-cov tools
* Suricata as a rust project
* rust coverage: remaps every rust subdirectory in fuzz
So that projects not using default fuzz_targets subdir
get the good remap, and hence the good coverage report
Diffstat (limited to 'infra/base-images')
-rwxr-xr-x | infra/base-images/base-builder/cargo | 6 | ||||
-rwxr-xr-x | infra/base-images/base-runner/coverage | 3 | ||||
-rw-r--r-- | infra/base-images/base-runner/gocoverage/llvm-cov-rel/llvm-profraw-relative.go | 180 |
3 files changed, 188 insertions, 1 deletions
diff --git a/infra/base-images/base-builder/cargo b/infra/base-images/base-builder/cargo index d83e1d2e..c60c7611 100755 --- a/infra/base-images/base-builder/cargo +++ b/infra/base-images/base-builder/cargo @@ -35,7 +35,11 @@ then # go into fuzz directory if not already the case cd fuzz || true fuzz_src_abspath=`pwd` - export RUSTFLAGS="$RUSTFLAGS --remap-path-prefix fuzz_targets=$fuzz_src_abspath/fuzz_targets" + # Default directory is fuzz_targets, but some projects like image-rs use fuzzers. + while read i; do + export RUSTFLAGS="$RUSTFLAGS --remap-path-prefix $i=$fuzz_src_abspath/$i" + # Bash while syntax so that we modify RUSTFLAGS in main shell instead of a subshell. + done <<< "$(ls */*.rs | cut -d/ -f1 | uniq)" # we do not want to trigger debug assertions and stops export RUSTFLAGS="$RUSTFLAGS -C debug-assertions=no" # do not optimize with --release, leading to Malformed instrumentation profile data diff --git a/infra/base-images/base-runner/coverage b/infra/base-images/base-runner/coverage index 54154212..40c31e07 100755 --- a/infra/base-images/base-runner/coverage +++ b/infra/base-images/base-runner/coverage @@ -99,6 +99,9 @@ function run_fuzz_target { return 0 fi + # If necessary translate to latest profraw version. + llvm-cov-rel $OUT/$target $profraw_file_mask tmp.profraw + mv tmp.profraw $profraw_file_mask llvm-profdata merge -j=1 -sparse $profraw_file_mask -o $profdata_file # Delete unnecessary and (potentially) large .profraw files. diff --git a/infra/base-images/base-runner/gocoverage/llvm-cov-rel/llvm-profraw-relative.go b/infra/base-images/base-runner/gocoverage/llvm-cov-rel/llvm-profraw-relative.go new file mode 100644 index 00000000..91556c7c --- /dev/null +++ b/infra/base-images/base-runner/gocoverage/llvm-cov-rel/llvm-profraw-relative.go @@ -0,0 +1,180 @@ +package main + +import ( + "debug/elf" + "encoding/binary" + "flag" + "fmt" + "io" + "io/ioutil" + "log" +) + +type ProfrawHeaderVersion7 struct { + ProfrawHeaderGeneric + BinaryIdsSize uint64 + DataSize uint64 + PaddingBytesBeforeCounters uint64 + CountersSize uint64 + PaddingBytesAfterCounters uint64 + NamesSize uint64 + CountersDelta uint64 + NamesDelta uint64 + ValueKindLast uint64 +} + +type ProfrawHeaderGeneric struct { + Magic uint64 + Version uint64 +} + +type ProfrawData struct { + NameRef uint64 + FuncHash uint64 + CounterPtr uint64 + FunctionPointer uint64 + Values uint64 + NumCounters uint32 + NumValueSites []uint16 +} + +const PROFRAW_HEADER_GENERIC_LEN = 16 +const PROFRAW_HEADER_7_LEN = 88 + +func parseProfrawHeaderGeneric(data []byte) (ProfrawHeaderGeneric, error) { + r := ProfrawHeaderGeneric{} + if len(data) < PROFRAW_HEADER_GENERIC_LEN { + return r, io.EOF + } + r.Magic = binary.LittleEndian.Uint64(data[:8]) + r.Version = binary.LittleEndian.Uint64(data[8:16]) + if r.Magic != 0xff6c70726f667281 { + return r, fmt.Errorf("Invalid magic %x", r.Magic) + } + return r, nil +} + +func relativizeAddress(data []byte, offset int, databegin uint64, sectPrfCnts uint64, sectPrfData uint64) { + value := binary.LittleEndian.Uint64(data[offset : offset+8]) + if value >= sectPrfCnts && value < sectPrfData { + // If the value is an address in the right section, + // Make it relative. + value = value - databegin + binary.LittleEndian.PutUint64(data[offset:offset+8], value) + } + +} + +func profrawDataLen(ipvklast uint64) int { + return 44 + 2*(int(ipvklast)+1) +} + +func relativizeProfraw(data []byte, sectPrfCnts uint64, sectPrfData uint64) (error, []byte) { + h := ProfrawHeaderVersion7{} + var err error + h.ProfrawHeaderGeneric, err = parseProfrawHeaderGeneric(data) + if err != nil { + return err, data + } + if h.Version == 5 { + // Upgrade from 5 to 7 by adding a zero binaryids in the header. + binary.LittleEndian.PutUint64(data[8:16], 7) + h.Version = 7 + data2 := make([]byte, len(data)+8) + copy(data2, data[0:16]) + copy(data2[24:], data[16:]) + data = data2 + } + if h.Version < 7 { + return fmt.Errorf("Invalid version for profraw file: %v", h.Version), data + } + // At one point clang-14 will update to 8, and more work will be needed. + if len(data) < PROFRAW_HEADER_7_LEN { + return io.EOF, data + } + h.BinaryIdsSize = binary.LittleEndian.Uint64(data[16:24]) + h.DataSize = binary.LittleEndian.Uint64(data[24:32]) + h.PaddingBytesBeforeCounters = binary.LittleEndian.Uint64(data[32:40]) + h.CountersSize = binary.LittleEndian.Uint64(data[40:48]) + h.PaddingBytesAfterCounters = binary.LittleEndian.Uint64(data[48:56]) + h.NamesSize = binary.LittleEndian.Uint64(data[56:64]) + h.CountersDelta = binary.LittleEndian.Uint64(data[64:72]) + h.NamesDelta = binary.LittleEndian.Uint64(data[72:80]) + h.ValueKindLast = binary.LittleEndian.Uint64(data[80:88]) + + if h.CountersDelta != sectPrfCnts-sectPrfData { + // Rust linking adds an offset ? not seen in readelf. + sectPrfData = h.CountersDelta - sectPrfCnts + sectPrfData + sectPrfCnts = h.CountersDelta + } + dataref := sectPrfData + relativizeAddress(data, 64, dataref, sectPrfCnts, sectPrfData) + + offset := PROFRAW_HEADER_7_LEN + int(h.BinaryIdsSize) + for i := uint64(0); i < h.DataSize; i++ { + if len(data) < offset+profrawDataLen(h.ValueKindLast) { + return io.EOF, data + } + d := ProfrawData{} + d.NameRef = binary.LittleEndian.Uint64(data[offset : offset+8]) + d.FuncHash = binary.LittleEndian.Uint64(data[offset+8 : offset+16]) + d.CounterPtr = binary.LittleEndian.Uint64(data[offset+16 : offset+24]) + d.FunctionPointer = binary.LittleEndian.Uint64(data[offset+24 : offset+32]) + d.Values = binary.LittleEndian.Uint64(data[offset+32 : offset+40]) + d.NumCounters = binary.LittleEndian.Uint32(data[offset+40 : offset+44]) + d.NumValueSites = make([]uint16, h.ValueKindLast+1) + for j := 0; j <= int(h.ValueKindLast); j++ { + d.NumValueSites[j] = binary.LittleEndian.Uint16(data[offset+44+2*j : offset+46+2*j]) + } + + relativizeAddress(data, offset+16, dataref, sectPrfCnts, sectPrfData) + // We need this because of CountersDelta -= sizeof(*SrcData); in __llvm_profile_merge_from_buffer. + dataref += uint64(profrawDataLen(h.ValueKindLast)) + + offset += profrawDataLen(h.ValueKindLast) + } + return nil, data +} + +func main() { + flag.Parse() + + if len(flag.Args()) != 3 { + log.Fatalf("needs exactly three arguments : binary, profraw, output") + } + + // First find llvm profile sections addresses in the elf. + f, err := elf.Open(flag.Args()[0]) + if err != nil { + log.Fatalf("failed to read elf: %v", err) + } + sectPrfCnts := uint64(0) + sectPrfData := uint64(0) + for i := range f.Sections { + if f.Sections[i].Name == "__llvm_prf_cnts" { + sectPrfCnts = f.Sections[i].Addr + } else if f.Sections[i].Name == "__llvm_prf_data" { + sectPrfData = f.Sections[i].Addr + // Maybe rather sectPrfCntsEnd as f.Sections[i].Addr + f.Sections[i].Size for __llvm_prf_cnts. + } + } + if sectPrfCnts == 0 || sectPrfData == 0 { + log.Fatalf("Elf has not __llvm_prf_cnts and __llvm_prf_data sections") + } + + // Process profraw file. + data, err := ioutil.ReadFile(flag.Args()[1]) + if err != nil { + log.Fatalf("failed to read file: %v", err) + } + err, data = relativizeProfraw(data, sectPrfCnts, sectPrfData) + if err != nil { + log.Fatalf("failed to process file: %v", err) + } + + // Write output file. + err = ioutil.WriteFile(flag.Args()[2], data, 0644) + if err != nil { + log.Fatalf("failed to write file: %v", err) + } +} |