2016-08-08 13:32:48 +00:00
|
|
|
// Copyright 2015/2016 syzkaller project authors. All rights reserved.
|
2015-10-14 14:55:09 +00:00
|
|
|
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package prog
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"math/rand"
|
|
|
|
"sort"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Calulation of call-to-call priorities.
|
|
|
|
// For a given pair of calls X and Y, the priority is our guess as to whether
|
|
|
|
// additional of call Y into a program containing call X is likely to give
|
|
|
|
// new coverage or not.
|
|
|
|
// The current algorithm has two components: static and dynamic.
|
|
|
|
// The static component is based on analysis of argument types. For example,
|
|
|
|
// if call X and call Y both accept fd[sock], then they are more likely to give
|
|
|
|
// new coverage together.
|
|
|
|
// The dynamic component is based on frequency of occurrence of a particular
|
|
|
|
// pair of syscalls in a single program in corpus. For example, if socket and
|
|
|
|
// connect frequently occur in programs together, we give higher priority to
|
|
|
|
// this pair of syscalls.
|
|
|
|
// Note: the current implementation is very basic, there is no theory behind any
|
|
|
|
// constants.
|
|
|
|
|
2017-09-14 17:25:01 +00:00
|
|
|
func (target *Target) CalculatePriorities(corpus []*Prog) [][]float32 {
|
|
|
|
static := target.calcStaticPriorities()
|
|
|
|
dynamic := target.calcDynamicPrio(corpus)
|
2015-10-14 14:55:09 +00:00
|
|
|
for i, prios := range static {
|
|
|
|
for j, p := range prios {
|
|
|
|
dynamic[i][j] *= p
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dynamic
|
|
|
|
}
|
|
|
|
|
2017-09-14 17:25:01 +00:00
|
|
|
func (target *Target) calcStaticPriorities() [][]float32 {
|
2018-08-01 18:33:33 +00:00
|
|
|
uses := target.calcResourceUsage()
|
|
|
|
prios := make([][]float32, len(target.Syscalls))
|
|
|
|
for i := range prios {
|
|
|
|
prios[i] = make([]float32, len(target.Syscalls))
|
|
|
|
}
|
|
|
|
for _, calls := range uses {
|
|
|
|
for c0, w0 := range calls {
|
|
|
|
for c1, w1 := range calls {
|
|
|
|
if c0 == c1 {
|
|
|
|
// Self-priority is assigned below.
|
|
|
|
continue
|
|
|
|
}
|
2019-09-10 08:06:53 +00:00
|
|
|
// The static priority is assigned based on the direction of arguments. A higher priority will be
|
|
|
|
// assigned when c0 is a call that produces a resource and c1 a call that uses that resource.
|
|
|
|
prios[c0][c1] += w0.inout*w1.in + 0.7*w0.inout*w1.inout
|
2015-10-14 14:55:09 +00:00
|
|
|
}
|
2018-08-01 18:33:33 +00:00
|
|
|
}
|
|
|
|
}
|
2019-09-10 08:06:53 +00:00
|
|
|
normalizePrio(prios)
|
|
|
|
// The value assigned for self-priority (call wrt itself) have to be high, but not too high.
|
2018-08-01 18:33:33 +00:00
|
|
|
for c0, pp := range prios {
|
2019-09-10 08:06:53 +00:00
|
|
|
pp[c0] = 0.9
|
2018-08-01 18:33:33 +00:00
|
|
|
}
|
|
|
|
return prios
|
|
|
|
}
|
|
|
|
|
2019-09-10 08:06:53 +00:00
|
|
|
func (target *Target) calcResourceUsage() map[string]map[int]weights {
|
|
|
|
uses := make(map[string]map[int]weights)
|
2018-08-01 18:33:33 +00:00
|
|
|
for _, c := range target.Syscalls {
|
2017-09-05 08:46:34 +00:00
|
|
|
ForeachType(c, func(t Type) {
|
2015-10-14 14:55:09 +00:00
|
|
|
switch a := t.(type) {
|
2017-09-05 08:46:34 +00:00
|
|
|
case *ResourceType:
|
2016-08-27 16:27:50 +00:00
|
|
|
if a.Desc.Name == "pid" || a.Desc.Name == "uid" || a.Desc.Name == "gid" {
|
2015-10-14 14:55:09 +00:00
|
|
|
// Pid/uid/gid usually play auxiliary role,
|
|
|
|
// but massively happen in some structs.
|
2019-09-10 08:06:53 +00:00
|
|
|
noteUsage(uses, c, 0.1, a.Dir(), "res%v", a.Desc.Name)
|
2015-10-14 14:55:09 +00:00
|
|
|
} else {
|
2016-08-27 16:27:50 +00:00
|
|
|
str := "res"
|
|
|
|
for i, k := range a.Desc.Kind {
|
|
|
|
str += "-" + k
|
|
|
|
w := 1.0
|
|
|
|
if i < len(a.Desc.Kind)-1 {
|
|
|
|
w = 0.2
|
|
|
|
}
|
2019-09-10 08:06:53 +00:00
|
|
|
noteUsage(uses, c, float32(w), a.Dir(), str)
|
2016-08-27 16:27:50 +00:00
|
|
|
}
|
2015-10-14 14:55:09 +00:00
|
|
|
}
|
2017-09-05 08:46:34 +00:00
|
|
|
case *PtrType:
|
|
|
|
if _, ok := a.Type.(*StructType); ok {
|
2019-09-10 08:06:53 +00:00
|
|
|
noteUsage(uses, c, 1.0, a.Dir(), "ptrto-%v", a.Type.Name())
|
2015-10-14 14:55:09 +00:00
|
|
|
}
|
2017-09-05 08:46:34 +00:00
|
|
|
if _, ok := a.Type.(*UnionType); ok {
|
2019-09-10 08:06:53 +00:00
|
|
|
noteUsage(uses, c, 1.0, a.Dir(), "ptrto-%v", a.Type.Name())
|
2015-12-29 14:00:57 +00:00
|
|
|
}
|
2017-09-05 08:46:34 +00:00
|
|
|
if arr, ok := a.Type.(*ArrayType); ok {
|
2019-09-10 08:06:53 +00:00
|
|
|
noteUsage(uses, c, 1.0, a.Dir(), "ptrto-%v", arr.Type.Name())
|
2015-12-29 14:00:57 +00:00
|
|
|
}
|
2017-09-05 08:46:34 +00:00
|
|
|
case *BufferType:
|
2015-10-14 14:55:09 +00:00
|
|
|
switch a.Kind {
|
2017-09-05 08:46:34 +00:00
|
|
|
case BufferBlobRand, BufferBlobRange, BufferText:
|
|
|
|
case BufferString:
|
2016-11-07 22:45:15 +00:00
|
|
|
if a.SubKind != "" {
|
2019-09-10 08:06:53 +00:00
|
|
|
noteUsage(uses, c, 0.2, a.Dir(), fmt.Sprintf("str-%v", a.SubKind))
|
2016-11-07 22:45:15 +00:00
|
|
|
}
|
2017-09-05 08:46:34 +00:00
|
|
|
case BufferFilename:
|
2019-09-10 08:06:53 +00:00
|
|
|
noteUsage(uses, c, 1.0, DirIn, "filename")
|
2015-10-14 14:55:09 +00:00
|
|
|
default:
|
|
|
|
panic("unknown buffer kind")
|
|
|
|
}
|
2017-09-05 08:46:34 +00:00
|
|
|
case *VmaType:
|
2019-09-10 08:06:53 +00:00
|
|
|
noteUsage(uses, c, 0.5, a.Dir(), "vma")
|
2017-09-05 08:46:34 +00:00
|
|
|
case *IntType:
|
2015-10-14 14:55:09 +00:00
|
|
|
switch a.Kind {
|
2017-09-05 08:46:34 +00:00
|
|
|
case IntPlain, IntFileoff, IntRange:
|
2015-10-14 14:55:09 +00:00
|
|
|
default:
|
|
|
|
panic("unknown int kind")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2018-08-01 18:33:33 +00:00
|
|
|
return uses
|
|
|
|
}
|
2015-12-30 17:24:26 +00:00
|
|
|
|
2019-09-10 08:06:53 +00:00
|
|
|
type weights struct {
|
|
|
|
in float32
|
|
|
|
inout float32
|
|
|
|
}
|
|
|
|
|
|
|
|
func noteUsage(uses map[string]map[int]weights, c *Syscall, weight float32, dir Dir, str string, args ...interface{}) {
|
2018-08-01 18:33:33 +00:00
|
|
|
id := fmt.Sprintf(str, args...)
|
|
|
|
if uses[id] == nil {
|
2019-09-10 08:06:53 +00:00
|
|
|
uses[id] = make(map[int]weights)
|
|
|
|
}
|
|
|
|
callWeight := uses[id][c.ID]
|
|
|
|
if dir != DirOut {
|
|
|
|
if weight > uses[id][c.ID].in {
|
|
|
|
callWeight.in = weight
|
|
|
|
}
|
2018-08-01 18:33:33 +00:00
|
|
|
}
|
2019-09-10 08:06:53 +00:00
|
|
|
if weight > uses[id][c.ID].inout {
|
|
|
|
callWeight.inout = weight
|
2015-10-14 14:55:09 +00:00
|
|
|
}
|
2019-09-10 08:06:53 +00:00
|
|
|
uses[id][c.ID] = callWeight
|
2015-10-14 14:55:09 +00:00
|
|
|
}
|
|
|
|
|
2017-09-14 17:25:01 +00:00
|
|
|
func (target *Target) calcDynamicPrio(corpus []*Prog) [][]float32 {
|
|
|
|
prios := make([][]float32, len(target.Syscalls))
|
2015-10-14 14:55:09 +00:00
|
|
|
for i := range prios {
|
2017-09-14 17:25:01 +00:00
|
|
|
prios[i] = make([]float32, len(target.Syscalls))
|
2015-10-14 14:55:09 +00:00
|
|
|
}
|
|
|
|
for _, p := range corpus {
|
2019-09-10 08:06:53 +00:00
|
|
|
for idx0, c0 := range p.Calls {
|
|
|
|
for _, c1 := range p.Calls[idx0+1:] {
|
2017-05-02 10:28:48 +00:00
|
|
|
id0 := c0.Meta.ID
|
|
|
|
id1 := c1.Meta.ID
|
|
|
|
prios[id0][id1] += 1.0
|
2015-10-14 14:55:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
normalizePrio(prios)
|
|
|
|
return prios
|
|
|
|
}
|
|
|
|
|
|
|
|
// normalizePrio assigns some minimal priorities to calls with zero priority,
|
|
|
|
// and then normalizes priorities to 0.1..1 range.
|
|
|
|
func normalizePrio(prios [][]float32) {
|
|
|
|
for _, prio := range prios {
|
|
|
|
max := float32(0)
|
|
|
|
min := float32(1e10)
|
|
|
|
nzero := 0
|
|
|
|
for _, p := range prio {
|
|
|
|
if max < p {
|
|
|
|
max = p
|
|
|
|
}
|
|
|
|
if p != 0 && min > p {
|
|
|
|
min = p
|
|
|
|
}
|
|
|
|
if p == 0 {
|
|
|
|
nzero++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if nzero != 0 {
|
|
|
|
min /= 2 * float32(nzero)
|
|
|
|
}
|
2018-08-30 13:58:05 +00:00
|
|
|
if min == max {
|
|
|
|
max = 0
|
|
|
|
}
|
2015-10-14 14:55:09 +00:00
|
|
|
for i, p := range prio {
|
|
|
|
if max == 0 {
|
|
|
|
prio[i] = 1
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if p == 0 {
|
|
|
|
p = min
|
|
|
|
}
|
|
|
|
p = (p-min)/(max-min)*0.9 + 0.1
|
|
|
|
if p > 1 {
|
|
|
|
p = 1
|
|
|
|
}
|
|
|
|
prio[i] = p
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ChooseTable allows to do a weighted choice of a syscall for a given syscall
|
|
|
|
// based on call-to-call priorities and a set of enabled syscalls.
|
|
|
|
type ChoiceTable struct {
|
2017-09-14 17:25:01 +00:00
|
|
|
target *Target
|
2015-10-15 15:58:37 +00:00
|
|
|
run [][]int
|
2017-09-05 08:46:34 +00:00
|
|
|
enabledCalls []*Syscall
|
|
|
|
enabled map[*Syscall]bool
|
2015-10-14 14:55:09 +00:00
|
|
|
}
|
|
|
|
|
2017-09-14 17:25:01 +00:00
|
|
|
func (target *Target) BuildChoiceTable(prios [][]float32, enabled map[*Syscall]bool) *ChoiceTable {
|
2015-12-27 11:20:00 +00:00
|
|
|
if enabled == nil {
|
2017-09-05 08:46:34 +00:00
|
|
|
enabled = make(map[*Syscall]bool)
|
2017-09-14 17:25:01 +00:00
|
|
|
for _, c := range target.Syscalls {
|
2015-12-27 11:20:00 +00:00
|
|
|
enabled[c] = true
|
|
|
|
}
|
2015-10-16 20:10:51 +00:00
|
|
|
}
|
2017-09-05 08:46:34 +00:00
|
|
|
var enabledCalls []*Syscall
|
2015-12-27 11:20:00 +00:00
|
|
|
for c := range enabled {
|
|
|
|
enabledCalls = append(enabledCalls, c)
|
2015-10-14 14:55:09 +00:00
|
|
|
}
|
prog: detect invalid target.Syscalls in BuildChoiceTable
Without this check programs may end up panicing in places far away
from the real cause. E.g.
worker# ./syz-fuzzer -executor=./syz-executor -name=vm-0 -arch=amd64 -manager=10.128.0.101:21386 -sandbox=setuid -procs=2 -v=0 -cover=true -debug=false -test=false
2004/02/03 12:11:11 fuzzer started
2004/02/03 12:11:11 dialing manager at 10.128.0.101:21386
2004/02/03 12:11:12 syscalls: 1
2004/02/03 12:11:12 code coverage: enabled
2004/02/03 12:11:12 comparison tracing: support is not implemented in syzkaller
2004/02/03 12:11:12 setuid sandbox: support is not implemented in syzkaller
2004/02/03 12:11:12 namespace sandbox: support is not implemented in syzkaller
2004/02/03 12:11:12 Android sandbox: support is not implemented in syzkaller
2004/02/03 12:11:12 fault injection: support is not implemented in syzkaller
2004/02/03 12:11:12 leak checking: support is not implemented in syzkaller
2004/02/03 12:11:12 net packet injection: enabled
2004/02/03 12:11:12 net device setup: support is not implemented in syzkaller
panic: invalid argument to Intn
goroutine 27 [running]:
math/rand.(*Rand).Intn(0xc000dff530, 0x0, 0x40)
/usr/local/go/src/math/rand/rand.go:169 +0x9c
github.com/google/syzkaller/prog.(*ChoiceTable).Choose(0xc000d92ec0, 0xc000dff530, 0xffffffffffffffff, 0xc000dff650)
/syzkaller/gopath/src/github.com/google/syzkaller/prog/prio.go:241 +0x1a0
github.com/google/syzkaller/prog.(*randGen).generateCall(0xc000e145a0, 0xc000c2a200, 0xc000ce7f80, 0x2348f1940, 0xc000ce3440, 0xc000e6ee01)
/syzkaller/gopath/src/github.com/google/syzkaller/prog/rand.go:451 +0x69
github.com/google/syzkaller/prog.(*Target).Generate(0xc00007f1e0, 0x8f8680, 0xc000ce3440, 0x1e, 0xc000d92ec0, 0x0)
/syzkaller/gopath/src/github.com/google/syzkaller/prog/generation.go:19 +0x2b2
main.(*Proc).loop(0xc000d92f40)
/syzkaller/gopath/src/github.com/google/syzkaller/syz-fuzzer/proc.go:93 +0x2a1
created by main.main
/syzkaller/gopath/src/github.com/google/syzkaller/syz-fuzzer/fuzzer.go:236 +0xfe2
2018-12-11 10:14:21 +00:00
|
|
|
if len(enabledCalls) == 0 {
|
|
|
|
panic(fmt.Sprintf("empty enabledCalls, len(target.Syscalls)=%v", len(target.Syscalls)))
|
|
|
|
}
|
2017-09-14 17:25:01 +00:00
|
|
|
run := make([][]int, len(target.Syscalls))
|
2015-10-14 14:55:09 +00:00
|
|
|
for i := range run {
|
2017-09-14 17:25:01 +00:00
|
|
|
if !enabled[target.Syscalls[i]] {
|
2015-10-14 14:55:09 +00:00
|
|
|
continue
|
|
|
|
}
|
2017-09-14 17:25:01 +00:00
|
|
|
run[i] = make([]int, len(target.Syscalls))
|
2015-10-14 14:55:09 +00:00
|
|
|
sum := 0
|
|
|
|
for j := range run[i] {
|
2017-09-14 17:25:01 +00:00
|
|
|
if enabled[target.Syscalls[j]] {
|
2017-12-13 15:35:54 +00:00
|
|
|
w := 1
|
|
|
|
if prios != nil {
|
|
|
|
w = int(prios[i][j] * 1000)
|
|
|
|
}
|
|
|
|
sum += w
|
2015-10-14 14:55:09 +00:00
|
|
|
}
|
|
|
|
run[i][j] = sum
|
|
|
|
}
|
|
|
|
}
|
2017-09-14 17:25:01 +00:00
|
|
|
return &ChoiceTable{target, run, enabledCalls, enabled}
|
2015-10-14 14:55:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (ct *ChoiceTable) Choose(r *rand.Rand, call int) int {
|
2015-10-15 15:58:37 +00:00
|
|
|
if call < 0 {
|
|
|
|
return ct.enabledCalls[r.Intn(len(ct.enabledCalls))].ID
|
|
|
|
}
|
2015-10-14 14:55:09 +00:00
|
|
|
run := ct.run[call]
|
|
|
|
if run == nil {
|
2015-10-15 15:58:37 +00:00
|
|
|
return ct.enabledCalls[r.Intn(len(ct.enabledCalls))].ID
|
2015-10-14 14:55:09 +00:00
|
|
|
}
|
|
|
|
for {
|
2017-12-13 15:31:51 +00:00
|
|
|
x := r.Intn(run[len(run)-1]) + 1
|
2015-10-14 14:55:09 +00:00
|
|
|
i := sort.SearchInts(run, x)
|
2017-12-13 15:31:51 +00:00
|
|
|
if ct.enabled[ct.target.Syscalls[i]] {
|
|
|
|
return i
|
2015-10-14 14:55:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|