mirror of
https://github.com/reactos/syzkaller.git
synced 2025-02-23 05:02:30 +00:00

Currently syzkaller uses per-call basic block (BB) coverage. This change implements edge (not-per-call) coverage. Edge coverage is more detailed than BB coverage as it captures not-taken branches, looping, etc. So it provides better feedback signal. This coverage is now called "signal" throughout the code. BB code coverage is also collected as it is required for visualisation. Not doing per-call coverage reduces corpus ~6-7x (from ~35K to ~5K), this has profound effect on fuzzing efficiency.
183 lines
3.6 KiB
Go
183 lines
3.6 KiB
Go
// Copyright 2015 syzkaller project authors. All rights reserved.
|
|
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
|
|
|
|
/* Package cover implements set operations on slices (arrays of coverage PCs). */
|
|
package cover
|
|
|
|
import (
|
|
"sort"
|
|
)
|
|
|
|
type Cover []uint32
|
|
|
|
func (a Cover) Len() int { return len(a) }
|
|
func (a Cover) Less(i, j int) bool { return a[i] < a[j] }
|
|
func (a Cover) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
|
|
const sent = ^uint32(0)
|
|
|
|
func Copy(cov Cover) Cover {
|
|
return append(Cover{}, cov...)
|
|
}
|
|
|
|
func RestorePC(pc uint32, base uint32) uint64 {
|
|
return uint64(base)<<32 + uint64(pc)
|
|
}
|
|
|
|
// Canonicalize sorts and removes duplicates.
|
|
func Canonicalize(cov []uint32) Cover {
|
|
sort.Sort(Cover(cov))
|
|
i := 0
|
|
last := sent
|
|
for _, pc := range cov {
|
|
if pc != last {
|
|
last = pc
|
|
cov[i] = pc
|
|
i++
|
|
}
|
|
}
|
|
return Cover(cov[:i])
|
|
}
|
|
|
|
func Difference(cov0, cov1 Cover) Cover {
|
|
return foreach(cov0, cov1, func(v0, v1 uint32) uint32 {
|
|
if v0 < v1 {
|
|
return v0
|
|
}
|
|
return sent
|
|
})
|
|
}
|
|
|
|
func SymmetricDifference(cov0, cov1 Cover) Cover {
|
|
return foreach(cov0, cov1, func(v0, v1 uint32) uint32 {
|
|
if v0 < v1 {
|
|
return v0
|
|
}
|
|
if v1 < v0 {
|
|
return v1
|
|
}
|
|
return sent
|
|
})
|
|
}
|
|
|
|
func Union(cov0, cov1 Cover) Cover {
|
|
return foreach(cov0, cov1, func(v0, v1 uint32) uint32 {
|
|
if v0 <= v1 {
|
|
return v0
|
|
}
|
|
return v1
|
|
})
|
|
}
|
|
|
|
func Intersection(cov0, cov1 Cover) Cover {
|
|
return foreach(cov0, cov1, func(v0, v1 uint32) uint32 {
|
|
if v0 == v1 {
|
|
return v0
|
|
}
|
|
return sent
|
|
})
|
|
}
|
|
|
|
func foreach(cov0, cov1 Cover, f func(uint32, uint32) uint32) Cover {
|
|
var res []uint32
|
|
for i0, i1 := 0, 0; i0 < len(cov0) || i1 < len(cov1); {
|
|
v0, v1 := sent, sent
|
|
if i0 < len(cov0) {
|
|
v0 = cov0[i0]
|
|
}
|
|
if i1 < len(cov1) {
|
|
v1 = cov1[i1]
|
|
}
|
|
if v0 <= v1 {
|
|
i0++
|
|
}
|
|
if v1 <= v0 {
|
|
i1++
|
|
}
|
|
if v := f(v0, v1); v != sent {
|
|
res = append(res, v)
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
|
|
// HasDifference returns true if cov0 has some coverage that is not present in cov1.
|
|
// This is called on fuzzer hot path.
|
|
func HasDifference(cov0, cov1 Cover) bool {
|
|
i1 := 0
|
|
for _, v0 := range cov0 {
|
|
for ; i1 < len(cov1) && cov1[i1] < v0; i1++ {
|
|
}
|
|
if i1 == len(cov1) || cov1[i1] > v0 {
|
|
return true
|
|
}
|
|
i1++
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Minimize returns a minimal set of inputs that give the same coverage as the full corpus.
|
|
func Minimize(corpus []Cover) []int {
|
|
inputs := make([]*minInput, len(corpus))
|
|
for i, cov := range corpus {
|
|
inputs[i] = &minInput{
|
|
idx: i,
|
|
cov: cov,
|
|
}
|
|
}
|
|
sort.Sort(minInputArray(inputs))
|
|
var min []int
|
|
covered := make(map[uint32]struct{})
|
|
for _, inp := range inputs {
|
|
hit := false
|
|
for _, pc := range inp.cov {
|
|
if !hit {
|
|
if _, ok := covered[pc]; !ok {
|
|
hit = true
|
|
min = append(min, inp.idx)
|
|
}
|
|
}
|
|
if hit {
|
|
covered[pc] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
return min
|
|
}
|
|
|
|
type minInput struct {
|
|
idx int
|
|
cov Cover
|
|
}
|
|
|
|
type minInputArray []*minInput
|
|
|
|
// Inputs with larger coverage come first.
|
|
func (a minInputArray) Len() int { return len(a) }
|
|
func (a minInputArray) Less(i, j int) bool { return len(a[i].cov) > len(a[j].cov) }
|
|
func (a minInputArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
|
|
func SignalNew(base map[uint32]struct{}, signal []uint32) bool {
|
|
for _, s := range signal {
|
|
if _, ok := base[s]; !ok {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func SignalDiff(base map[uint32]struct{}, signal []uint32) (diff []uint32) {
|
|
for _, s := range signal {
|
|
if _, ok := base[s]; !ok {
|
|
diff = append(diff, s)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func SignalAdd(base map[uint32]struct{}, signal []uint32) {
|
|
for _, s := range signal {
|
|
base[s] = struct{}{}
|
|
}
|
|
}
|