aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pkg/instance/instance.go14
-rw-r--r--pkg/ipc/ipc.go22
-rw-r--r--pkg/mgrconfig/config.go5
-rw-r--r--pkg/rpctype/rpctype.go10
-rw-r--r--syz-fuzzer/fuzzer.go22
-rw-r--r--syz-fuzzer/proc.go17
-rw-r--r--syz-manager/manager.go33
-rw-r--r--syz-manager/rpc.go1
8 files changed, 86 insertions, 38 deletions
diff --git a/pkg/instance/instance.go b/pkg/instance/instance.go
index 665281a47..c225f9696 100644
--- a/pkg/instance/instance.go
+++ b/pkg/instance/instance.go
@@ -452,6 +452,7 @@ type FuzzerCmdArgs struct {
Test bool
Runtest bool
Slowdown int
+ RawCover bool
}
func FuzzerCmd(args *FuzzerCmdArgs) string {
@@ -470,11 +471,16 @@ func FuzzerCmd(args *FuzzerCmdArgs) string {
if args.Verbosity != 0 {
verbosityArg = fmt.Sprintf(" -vv=%v", args.Verbosity)
}
- optionalArg := ""
+ flags := []tool.Flag{}
if args.Slowdown > 0 {
- optionalArg = " " + tool.OptionalFlags([]tool.Flag{
- {Name: "slowdown", Value: fmt.Sprint(args.Slowdown)},
- })
+ flags = append(flags, tool.Flag{Name: "slowdown", Value: fmt.Sprint(args.Slowdown)})
+ }
+ if args.RawCover {
+ flags = append(flags, tool.Flag{Name: "raw_cover", Value: "true"})
+ }
+ optionalArg := ""
+ if len(flags) > 0 {
+ optionalArg += " " + tool.OptionalFlags(flags)
}
return fmt.Sprintf("%v -executor=%v -name=%v -arch=%v%v -manager=%v -sandbox=%v"+
" -procs=%v -cover=%v -debug=%v -test=%v%v%v%v",
diff --git a/pkg/ipc/ipc.go b/pkg/ipc/ipc.go
index 5bf4738ca..6b25d4af4 100644
--- a/pkg/ipc/ipc.go
+++ b/pkg/ipc/ipc.go
@@ -289,7 +289,7 @@ func (env *Env) Exec(opts *ExecOpts, p *prog.Prog) (output []byte, info *ProgInf
return
}
- info, err0 = env.parseOutput(p)
+ info, err0 = env.parseOutput(p, opts)
if info != nil && env.config.Flags&FlagSignal == 0 {
addFallbackSignal(p, info)
}
@@ -323,7 +323,7 @@ func addFallbackSignal(p *prog.Prog, info *ProgInfo) {
}
}
-func (env *Env) parseOutput(p *prog.Prog) (*ProgInfo, error) {
+func (env *Env) parseOutput(p *prog.Prog, opts *ExecOpts) (*ProgInfo, error) {
out := env.out
ncmd, ok := readUint32(&out)
if !ok {
@@ -372,19 +372,27 @@ func (env *Env) parseOutput(p *prog.Prog) (*ProgInfo, error) {
if len(extraParts) == 0 {
return info, nil
}
- info.Extra = convertExtra(extraParts)
+ info.Extra = convertExtra(extraParts, opts.Flags&FlagDedupCover > 0)
return info, nil
}
-func convertExtra(extraParts []CallInfo) CallInfo {
+func convertExtra(extraParts []CallInfo, dedupCover bool) CallInfo {
var extra CallInfo
- extraCover := make(cover.Cover)
+ if dedupCover {
+ extraCover := make(cover.Cover)
+ for _, part := range extraParts {
+ extraCover.Merge(part.Cover)
+ }
+ extra.Cover = extraCover.Serialize()
+ } else {
+ for _, part := range extraParts {
+ extra.Cover = append(extra.Cover, part.Cover...)
+ }
+ }
extraSignal := make(signal.Signal)
for _, part := range extraParts {
- extraCover.Merge(part.Cover)
extraSignal.Merge(signal.FromRaw(part.Signal, 0))
}
- extra.Cover = extraCover.Serialize()
extra.Signal = make([]uint32, len(extraSignal))
i := 0
for s := range extraSignal {
diff --git a/pkg/mgrconfig/config.go b/pkg/mgrconfig/config.go
index 5d4a9f8f5..c13e6f452 100644
--- a/pkg/mgrconfig/config.go
+++ b/pkg/mgrconfig/config.go
@@ -132,6 +132,11 @@ type Config struct {
// eg. "0xffffffff81000000:0x10\n"
CovFilter covFilterCfg `json:"cover_filter,omitempty"`
+ // For each prog in the corpus, remember the raw array of PCs obtained from the kernel.
+ // It can be useful for debugging syzkaller descriptions and syzkaller itself.
+ // Disabled by default as it slows down fuzzing.
+ RawCover bool `json:"raw_cover"`
+
// Reproduce, localize and minimize crashers (default: true).
Reproduce bool `json:"reproduce"`
diff --git a/pkg/rpctype/rpctype.go b/pkg/rpctype/rpctype.go
index cb2f43e76..f1d889a20 100644
--- a/pkg/rpctype/rpctype.go
+++ b/pkg/rpctype/rpctype.go
@@ -14,10 +14,12 @@ import (
)
type Input struct {
- Call string
- Prog []byte
- Signal signal.Serial
- Cover []uint32
+ Call string
+ Prog []byte
+ Signal signal.Serial
+ Cover []uint32
+ CallID int // seq number of call in the prog to which the item is related (-1 for extra)
+ RawCover []uint32
}
type Candidate struct {
diff --git a/syz-fuzzer/fuzzer.go b/syz-fuzzer/fuzzer.go
index 8e916f034..f87c7e740 100644
--- a/syz-fuzzer/fuzzer.go
+++ b/syz-fuzzer/fuzzer.go
@@ -48,6 +48,7 @@ type Fuzzer struct {
faultInjectionEnabled bool
comparisonTracingEnabled bool
+ fetchRawCover bool
corpusMu sync.RWMutex
corpus []*prog.Prog
@@ -138,14 +139,15 @@ func main() {
debug.SetGCPercent(50)
var (
- flagName = flag.String("name", "test", "unique name for manager")
- flagOS = flag.String("os", runtime.GOOS, "target OS")
- flagArch = flag.String("arch", runtime.GOARCH, "target arch")
- flagManager = flag.String("manager", "", "manager rpc address")
- flagProcs = flag.Int("procs", 1, "number of parallel test processes")
- flagOutput = flag.String("output", "stdout", "write programs to none/stdout/dmesg/file")
- flagTest = flag.Bool("test", false, "enable image testing mode") // used by syz-ci
- flagRunTest = flag.Bool("runtest", false, "enable program testing mode") // used by pkg/runtest
+ flagName = flag.String("name", "test", "unique name for manager")
+ flagOS = flag.String("os", runtime.GOOS, "target OS")
+ flagArch = flag.String("arch", runtime.GOARCH, "target arch")
+ flagManager = flag.String("manager", "", "manager rpc address")
+ flagProcs = flag.Int("procs", 1, "number of parallel test processes")
+ flagOutput = flag.String("output", "stdout", "write programs to none/stdout/dmesg/file")
+ flagTest = flag.Bool("test", false, "enable image testing mode") // used by syz-ci
+ flagRunTest = flag.Bool("runtest", false, "enable program testing mode") // used by pkg/runtest
+ flagRawCover = flag.Bool("raw_cover", false, "fetch raw coverage")
)
defer tool.Init()()
outputType := parseOutputType(*flagOutput)
@@ -160,6 +162,9 @@ func main() {
if err != nil {
log.Fatalf("failed to create default ipc config: %v", err)
}
+ if *flagRawCover {
+ execOpts.Flags &^= ipc.FlagDedupCover
+ }
timeouts := config.Timeouts
sandbox := ipc.FlagsToSandbox(config.Flags)
shutdown := make(chan struct{})
@@ -264,6 +269,7 @@ func main() {
comparisonTracingEnabled: r.CheckResult.Features[host.FeatureComparisons].Enabled,
corpusHashes: make(map[hash.Sig]struct{}),
checkResult: r.CheckResult,
+ fetchRawCover: *flagRawCover,
}
gateCallback := fuzzer.useBugFrames(r, *flagProcs)
fuzzer.gate = ipc.NewGate(2**flagProcs, gateCallback)
diff --git a/syz-fuzzer/proc.go b/syz-fuzzer/proc.go
index b15560b6a..d46df123b 100644
--- a/syz-fuzzer/proc.go
+++ b/syz-fuzzer/proc.go
@@ -122,6 +122,7 @@ func (proc *Proc) triageInput(item *WorkTriage) {
)
// Compute input coverage and non-flaky signal for minimization.
notexecuted := 0
+ rawCover := []uint32{}
for i := 0; i < signalRuns; i++ {
info := proc.executeRaw(proc.execOptsCover, item.p, StatTriage)
if !reexecutionSuccess(info, &item.info, item.call) {
@@ -133,6 +134,9 @@ func (proc *Proc) triageInput(item *WorkTriage) {
continue
}
thisSignal, thisCover := getSignalAndCover(item.p, info, item.call)
+ if len(rawCover) == 0 && proc.fuzzer.fetchRawCover {
+ rawCover = append([]uint32{}, thisCover...)
+ }
newSignal = newSignal.Intersection(thisSignal)
// Without !minimized check manager starts losing some considerable amount
// of coverage after each restart. Mechanics of this are not completely clear.
@@ -164,10 +168,12 @@ func (proc *Proc) triageInput(item *WorkTriage) {
log.Logf(2, "added new input for %v to corpus:\n%s", logCallName, data)
proc.fuzzer.sendInputToManager(rpctype.Input{
- Call: callName,
- Prog: data,
- Signal: inputSignal.Serialize(),
- Cover: inputCover.Serialize(),
+ Call: callName,
+ CallID: item.call,
+ Prog: data,
+ Signal: inputSignal.Serialize(),
+ Cover: inputCover.Serialize(),
+ RawCover: rawCover,
})
proc.fuzzer.addInputToCorpus(item.p, inputSignal, sig)
@@ -303,9 +309,6 @@ func (proc *Proc) randomCollide(origP *prog.Prog) *prog.Prog {
}
func (proc *Proc) executeRaw(opts *ipc.ExecOpts, p *prog.Prog, stat Stat) *ipc.ProgInfo {
- if opts.Flags&ipc.FlagDedupCover == 0 {
- log.Fatalf("dedup cover is not enabled")
- }
proc.fuzzer.checkDisabledCalls(p)
// Limit concurrency window and do leak checking once in a while.
diff --git a/syz-manager/manager.go b/syz-manager/manager.go
index 18ccef497..9d5662188 100644
--- a/syz-manager/manager.go
+++ b/syz-manager/manager.go
@@ -94,11 +94,17 @@ type Manager struct {
modulesInitialized bool
}
+type CorpusItemUpdate struct {
+ CallID int
+ RawCover []uint32
+}
+
type CorpusItem struct {
- Call string
- Prog []byte
- Signal signal.Serial
- Cover []uint32
+ Call string
+ Prog []byte
+ Signal signal.Serial
+ Cover []uint32
+ Updates []CorpusItemUpdate
}
func (item *CorpusItem) RPCInput() rpctype.Input {
@@ -689,6 +695,7 @@ func (mgr *Manager) runInstanceInner(index int, instanceName string) (*report.Re
Test: false,
Runtest: false,
Slowdown: mgr.cfg.Timeouts.Slowdown,
+ RawCover: mgr.cfg.RawCover,
}
cmd := instance.FuzzerCmd(args)
outc, errc, err := inst.Run(mgr.cfg.Timeouts.VMRunningTime, mgr.vmStop, cmd)
@@ -1164,6 +1171,10 @@ func (mgr *Manager) newInput(inp rpctype.Input, sign signal.Signal) bool {
if mgr.saturatedCalls[inp.Call] {
return false
}
+ update := CorpusItemUpdate{
+ CallID: inp.CallID,
+ RawCover: inp.RawCover,
+ }
sig := hash.String(inp.Prog)
if old, ok := mgr.corpus[sig]; ok {
// The input is already present, but possibly with diffent signal/coverage/call.
@@ -1173,13 +1184,19 @@ func (mgr *Manager) newInput(inp rpctype.Input, sign signal.Signal) bool {
cov.Merge(old.Cover)
cov.Merge(inp.Cover)
old.Cover = cov.Serialize()
+ const maxUpdates = 32
+ old.Updates = append(old.Updates, update)
+ if len(old.Updates) > maxUpdates {
+ old.Updates = old.Updates[:maxUpdates]
+ }
mgr.corpus[sig] = old
} else {
mgr.corpus[sig] = CorpusItem{
- Call: inp.Call,
- Prog: inp.Prog,
- Signal: inp.Signal,
- Cover: inp.Cover,
+ Call: inp.Call,
+ Prog: inp.Prog,
+ Signal: inp.Signal,
+ Cover: inp.Cover,
+ Updates: []CorpusItemUpdate{update},
}
mgr.corpusDB.Save(sig, inp.Prog, 0)
if err := mgr.corpusDB.Flush(); err != nil {
diff --git a/syz-manager/rpc.go b/syz-manager/rpc.go
index 8f9eb98b8..397484783 100644
--- a/syz-manager/rpc.go
+++ b/syz-manager/rpc.go
@@ -302,6 +302,7 @@ func (serv *RPCServer) NewInput(a *rpctype.NewInputArgs, r *int) error {
serv.stats.corpusSignal.set(serv.corpusSignal.Len())
a.Input.Cover = nil // Don't send coverage back to all fuzzers.
+ a.Input.RawCover = nil
for _, other := range serv.fuzzers {
if other == f || other.rotated {
continue