Skip to content

Commit a1a73a9

Browse files
matankalinadBransky
authored andcommitted
syz-verifier: Transform syz-verifier from coverage-discovery fuzzer to kernel comparison tool for pre-learned corpus.
No longer learns or generates programs. Instead: - Takes existing saturated corpus.db as input (-corpus flag) - Executes each program across multiple kernel versions in parallel - Compares syscall errno per call to detect behavioral differences - Logs mismatches with full program sequence and call-by-call results
1 parent 76dc7e5 commit a1a73a9

File tree

4 files changed

+173
-482
lines changed

4 files changed

+173
-482
lines changed

pkg/manager/corpus.go

Lines changed: 0 additions & 89 deletions
This file was deleted.

syz-manager/manager.go

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,20 +1032,68 @@ func (mgr *Manager) addNewCandidates(candidates []fuzzer.Candidate) {
10321032
}
10331033

10341034
func (mgr *Manager) minimizeCorpusLocked() {
1035-
cm := &manager.CorpusMinimizer{
1036-
Corpus: mgr.corpus,
1037-
CorpusDB: mgr.corpusDB,
1038-
Cover: mgr.cfg.Cover,
1039-
LastMinCorpus: mgr.lastMinCorpus,
1040-
SaturatedCalls: mgr.saturatedCalls,
1041-
DisabledHashes: mgr.disabledHashes,
1042-
PhaseCheck: func() bool {
1043-
return mgr.phase >= phaseTriagedCorpus
1044-
},
1035+
// Don't minimize corpus until we have triaged all inputs from it.
1036+
// During corpus triage it would happen very often since we are actively adding inputs,
1037+
// and presumably the persistent corpus was reasonably minimial, and we don't use it for fuzzing yet.
1038+
if mgr.phase < phaseTriagedCorpus {
1039+
return
1040+
}
1041+
currSize := mgr.corpus.StatProgs.Val()
1042+
if currSize <= mgr.lastMinCorpus*103/100 {
1043+
return
1044+
}
1045+
mgr.corpus.Minimize(mgr.cfg.Cover)
1046+
newSize := mgr.corpus.StatProgs.Val()
1047+
1048+
log.Logf(1, "minimized corpus: %v -> %v", currSize, newSize)
1049+
mgr.lastMinCorpus = newSize
1050+
1051+
// From time to time we get corpus explosion due to different reason:
1052+
// generic bugs, per-OS bugs, problems with fallback coverage, kcov bugs, etc.
1053+
// This has bad effect on the instance and especially on instances
1054+
// connected via hub. Do some per-syscall sanity checking to prevent this.
1055+
for call, info := range mgr.corpus.CallCover() {
1056+
if mgr.cfg.Cover {
1057+
// If we have less than 1K inputs per this call,
1058+
// accept all new inputs unconditionally.
1059+
if info.Count < 1000 {
1060+
continue
1061+
}
1062+
// If we have more than 3K already, don't accept any more.
1063+
// Between 1K and 3K look at amount of coverage we are getting from these programs.
1064+
// Empirically, real coverage for the most saturated syscalls is ~30-60
1065+
// per program (even when we have a thousand of them). For explosion
1066+
// case coverage tend to be much lower (~0.3-5 per program).
1067+
if info.Count < 3000 && len(info.Cover)/info.Count >= 10 {
1068+
continue
1069+
}
1070+
} else {
1071+
// If we don't have real coverage, signal is weak.
1072+
// If we have more than several hundreds, there is something wrong.
1073+
if info.Count < 300 {
1074+
continue
1075+
}
1076+
}
1077+
if mgr.saturatedCalls[call] {
1078+
continue
1079+
}
1080+
mgr.saturatedCalls[call] = true
1081+
log.Logf(0, "coverage for %v has saturated, not accepting more inputs", call)
10451082
}
1083+
10461084
mgr.corpusDBMu.Lock()
10471085
defer mgr.corpusDBMu.Unlock()
1048-
mgr.lastMinCorpus = cm.Minimize()
1086+
for key := range mgr.corpusDB.Records {
1087+
ok1 := mgr.corpus.Item(key) != nil
1088+
_, ok2 := mgr.disabledHashes[key]
1089+
if !ok1 && !ok2 {
1090+
mgr.corpusDB.Delete(key)
1091+
}
1092+
}
1093+
if err := mgr.corpusDB.Flush(); err != nil {
1094+
log.Fatalf("failed to save corpus database: %v", err)
1095+
}
1096+
mgr.corpusDB.BumpVersion(manager.CurrentDBVersion)
10491097
}
10501098

10511099
func setGuiltyFiles(crash *dashapi.Crash, report *report.Report) {

syz-verifier/main.go

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,8 @@ import (
1111
"os"
1212

1313
"github.com/google/syzkaller/pkg/flatrpc"
14-
"github.com/google/syzkaller/pkg/fuzzer"
1514
"github.com/google/syzkaller/pkg/fuzzer/queue"
1615
"github.com/google/syzkaller/pkg/log"
17-
"github.com/google/syzkaller/pkg/manager"
1816
"github.com/google/syzkaller/pkg/mgrconfig"
1917
"github.com/google/syzkaller/pkg/osutil"
2018
"github.com/google/syzkaller/pkg/report"
@@ -39,8 +37,6 @@ func Setup(name string, cfg *mgrconfig.Config, debug bool) (*Kernel, error) {
3937
cfg: cfg,
4038
crashes: make(chan *report.Report, 128),
4139
servStats: rpcserver.NewNamedStats(name),
42-
candidates: make(chan []fuzzer.Candidate),
43-
reportGenerator: manager.ReportGeneratorCache(cfg),
4440
enabledSyscalls: make(chan map[*prog.Syscall]bool, 1),
4541
features: make(chan flatrpc.Feature, 1),
4642
}
@@ -75,8 +71,7 @@ func main() {
7571
var cfgs tool.CfgsFlag
7672
flag.Var(&cfgs, "configs", "[MANDATORY] list of at least two kernel-specific comma-sepatated configuration files")
7773
flagDebug := flag.Bool("debug", false, "dump all VM output to console")
78-
// flagAddress := flag.String("address", "127.0.0.1:8080", "http address for monitoring")
79-
// flagReruns := flag.Int("rerun", 3, "number of time program is rerun when a mismatch is found")
74+
flagCorpus := flag.String("corpus", "", "path to corpus.db file (default: <workdir>/corpus.db)")
8075
flag.Parse()
8176

8277
kernels := make([]*Kernel, len(cfgs))
@@ -101,13 +96,11 @@ func main() {
10196
os.Exit(1)
10297
}
10398
workdir := kernels[0].cfg.Workdir
104-
reqMaxSignal := make(chan int, len(kernels))
10599
sources := make(map[int]*queue.PlainQueue)
106100
for idx, kernel := range kernels {
107101
if kernel.cfg.Workdir != workdir {
108102
log.Fatalf("all kernel configurations must have the same workdir, got %q and %q", workdir, kernel.cfg.Workdir)
109103
}
110-
kernel.reqMaxSignal = reqMaxSignal
111104
sources[idx] = queue.Plain()
112105
kernel.source = sources[idx]
113106
}
@@ -117,12 +110,11 @@ func main() {
117110
log.Logf(0, "initialized %d sources", len(sources))
118111

119112
vrf := &Verifier{
120-
kernels: kernels,
121-
cfg: kernels[0].cfg, // for now take the first kernel's config
122-
corpusPreload: make(chan []fuzzer.Candidate),
123-
disabledHashes: make(map[string]struct{}),
124-
target: kernels[0].cfg.Target,
125-
sources: sources,
113+
kernels: kernels,
114+
cfg: kernels[0].cfg, // for now take the first kernel's config
115+
target: kernels[0].cfg.Target,
116+
sources: sources,
117+
corpusPath: *flagCorpus,
126118
}
127119

128120
ctx := vm.ShutdownCtx()

0 commit comments

Comments
 (0)