aboutsummaryrefslogtreecommitdiff
path: root/infra
diff options
context:
space:
mode:
authorCatena cyber <35799796+catenacyber@users.noreply.github.com>2021-09-27 17:11:36 +0200
committerGitHub <noreply@github.com>2021-09-27 11:11:36 -0400
commit5c386a4858611e2e4b83dadcfc03e6701aafba6f (patch)
tree31fce885e48ed8fd5da522d4da86e2f55274d8da /infra
parent13135d51aa20fd488b1f07abb818c0a34995bfe2 (diff)
downloadoss-fuzz-5c386a4858611e2e4b83dadcfc03e6701aafba6f.tar.gz
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')
-rwxr-xr-xinfra/base-images/base-builder/cargo6
-rwxr-xr-xinfra/base-images/base-runner/coverage3
-rw-r--r--infra/base-images/base-runner/gocoverage/llvm-cov-rel/llvm-profraw-relative.go180
3 files changed, 188 insertions, 1 deletions
diff --git a/infra/base-images/base-builder/cargo b/infra/base-images/base-builder/cargo
index d83e1d2e7..c60c7611b 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 54154212d..40c31e076 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 000000000..91556c7c8
--- /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)
+ }
+}