// Copyright 2019 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 prog import ( "bytes" "fmt" "math/rand" "sort" "testing" "github.com/google/go-cmp/cmp" ) func TestRotationResourceless(t *testing.T) { target, rs, _ := initRandomTargetTest(t, "test", "64") calls := map[*Syscall]bool{ target.SyscallMap["test$int"]: true, } got := MakeRotator(target, calls, rand.New(rs)).Select() if diff := cmp.Diff(calls, got); diff != "" { t.Fatal(diff) } } func TestRotationRandom(t *testing.T) { target, rs, _ := initTest(t) for _, ncalls := range []int{10, 100, 1000, 1e9} { rnd := rand.New(rand.NewSource(rs.Int63())) t.Run(fmt.Sprint(ncalls), func(t *testing.T) { t.Parallel() calls0 := selectCalls(target, rnd, ncalls) calls := MakeRotator(target, calls0, rnd).Select() for call := range calls { if !calls0[call] { t.Errorf("selected disabled syscall %v", call.Name) } } buf := new(bytes.Buffer) var array []*Syscall for call := range calls { array = append(array, call) } sort.Slice(array, func(i, j int) bool { return array[i].Name < array[j].Name }) for _, call := range array { fmt.Fprintf(buf, "%v\n", call.Name) } t.Logf("calls %v->%v:\n%s", len(calls0), len(calls), buf.Bytes()) }) } } func TestRotationCoverage(t *testing.T) { target, rs, _ := initTest(t) calls := make(map[*Syscall]bool) counters := make(map[string]int) for _, call := range target.Syscalls { if call.Attrs.Disabled || call.Attrs.Automatic { continue } calls[call] = true counters[call.Name] = 0 } rotator := MakeRotator(target, calls, rand.New(rs)) nextIter: for iter := 0; iter < 1e4; iter++ { for call := range rotator.Select() { counters[call.Name]++ } for _, count := range counters { if count == 0 { continue nextIter } } break } type pair struct { name string count int } var pairs []pair remain := len(counters) for name, count := range counters { pairs = append(pairs, pair{name, count}) if count != 0 { remain-- } } sort.Slice(pairs, func(i, j int) bool { if pairs[i].count != pairs[j].count { return pairs[i].count > pairs[j].count } return pairs[i].name < pairs[j].name }) for i, pair := range pairs { t.Logf("# %4d: % 4d %v", i, pair.count, pair.name) } if remain != 0 { t.Fatalf("uncovered syscalls: %v", remain) } } func selectCalls(target *Target, rnd *rand.Rand, ncalls int) map[*Syscall]bool { retry: calls := make(map[*Syscall]bool) for _, call := range target.Syscalls { calls[call] = true } for { for { remove := 0 switch { case len(calls) > ncalls+1000: remove = 100 case len(calls) > ncalls+50: remove = 20 case len(calls) > ncalls: remove = 1 default: return calls } var array []*Syscall for call := range calls { array = append(array, call) } sort.Slice(array, func(i, j int) bool { return array[i].ID < array[j].ID }) rnd.Shuffle(len(calls), func(i, j int) { array[i], array[j] = array[j], array[i] }) for _, call := range array[:remove] { delete(calls, call) } calls, _ = target.transitivelyEnabled(calls) if len(calls) == 0 { goto retry } } } } func TestRotationDeterminism(t *testing.T) { target, rs, _ := initTest(t) calls := make(map[*Syscall]bool) for _, call := range target.Syscalls { calls[call] = true } seed := rs.Int63() rnd0 := rand.New(rand.NewSource(seed)) calls0 := MakeRotator(target, calls, rnd0).Select() rnd1 := rand.New(rand.NewSource(seed)) calls1 := MakeRotator(target, calls, rnd1).Select() if diff := cmp.Diff(calls0, calls1); diff != "" { t.Fatal(diff) } }