aboutsummaryrefslogtreecommitdiffstats
path: root/syz-manager
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2020-03-07 13:12:35 +0100
committerDmitry Vyukov <dvyukov@google.com>2020-03-13 13:16:53 +0100
commit9b1f3e665308ee2ddd5b3f35a078219b5c509cdb (patch)
tree56e177dcb9b249381d27abacec5e59e9d2cf410f /syz-manager
parent05359321bb37f035e55ccfad2cc36b0ea3b50998 (diff)
prog: control program length
We have _some_ limits on program length, but they are really soft. When we ask to generate a program with 10 calls, sometimes we get 100-150 calls. There are also no checks when we accept external programs from corpus/hub. Issue #1630 contains an example where this crashes VM (executor limit on number of 1000 resources is violated). Larger programs also harm the process overall (slower, consume more memory, lead to monster reproducers, etc). Add a set of measure for hard control over program length. Ensure that generated/mutated programs are not too long; drop too long programs coming from corpus/hub in manager; drop too long programs in hub. As a bonus ensure that mutation don't produce programs with 0 calls (which is currently possible and happens). Fixes #1630
Diffstat (limited to 'syz-manager')
-rw-r--r--syz-manager/hub.go3
-rw-r--r--syz-manager/manager.go22
-rw-r--r--syz-manager/rpc.go11
3 files changed, 25 insertions, 11 deletions
diff --git a/syz-manager/hub.go b/syz-manager/hub.go
index ff0f26066..51937b536 100644
--- a/syz-manager/hub.go
+++ b/syz-manager/hub.go
@@ -171,7 +171,8 @@ func (hc *HubConnector) processProgs(progs [][]byte) int {
dropped := 0
candidates := make([][]byte, 0, len(progs))
for _, inp := range progs {
- if _, err := hc.target.Deserialize(inp, prog.NonStrict); err != nil {
+ p, err := hc.target.Deserialize(inp, prog.NonStrict)
+ if err != nil || len(p.Calls) > prog.MaxCalls {
dropped++
continue
}
diff --git a/syz-manager/manager.go b/syz-manager/manager.go
index e2a1be2ba..2a0b7dd48 100644
--- a/syz-manager/manager.go
+++ b/syz-manager/manager.go
@@ -102,7 +102,7 @@ const (
phaseTriagedHub
)
-const currentDBVersion = 3
+const currentDBVersion = 4
type Crash struct {
vmIndex int
@@ -463,23 +463,30 @@ func (mgr *Manager) loadCorpus() {
// Version 2->3: big-endian hints.
smashed = false
fallthrough
+ case 3:
+ // Version 3->4: to shake things up.
+ minimized = false
+ fallthrough
case currentDBVersion:
}
syscalls := make(map[int]bool)
for _, id := range mgr.checkResult.EnabledCalls[mgr.cfg.Sandbox] {
syscalls[id] = true
}
- deleted := 0
+ broken, tooLong := 0, 0
for key, rec := range mgr.corpusDB.Records {
p, err := mgr.target.Deserialize(rec.Val, prog.NonStrict)
if err != nil {
- if deleted < 10 {
- log.Logf(0, "deleting broken program: %v\n%s", err, rec.Val)
- }
mgr.corpusDB.Delete(key)
- deleted++
+ broken++
continue
}
+ if len(p.Calls) > prog.MaxCalls {
+ mgr.corpusDB.Delete(key)
+ tooLong++
+ continue
+ }
+
disabled := false
for _, c := range p.Calls {
if !syscalls[c.Meta.ID] {
@@ -501,7 +508,8 @@ func (mgr *Manager) loadCorpus() {
})
}
mgr.fresh = len(mgr.corpusDB.Records) == 0
- log.Logf(0, "%-24v: %v (%v deleted)", "corpus", len(mgr.candidates), deleted)
+ log.Logf(0, "%-24v: %v (deleted %v broken, %v too long)",
+ "corpus", len(mgr.candidates), broken, tooLong)
// Now this is ugly.
// We duplicate all inputs in the corpus and shuffle the second part.
diff --git a/syz-manager/rpc.go b/syz-manager/rpc.go
index c60b7a9a5..91e31dbd9 100644
--- a/syz-manager/rpc.go
+++ b/syz-manager/rpc.go
@@ -165,7 +165,7 @@ func (serv *RPCServer) selectInputs(enabled map[string]bool, inputs0 []rpctype.R
inputs []rpctype.RPCInput, signal signal.Signal) {
signal = signal0.Copy()
for _, inp := range inputs0 {
- calls, err := prog.CallSet(inp.Prog)
+ calls, _, err := prog.CallSet(inp.Prog)
if err != nil {
panic(fmt.Sprintf("rotateInputs: CallSet failed: %v\n%s", err, inp.Prog))
}
@@ -210,11 +210,16 @@ func (serv *RPCServer) NewInput(a *rpctype.NewInputArgs, r *int) error {
inputSignal := a.Signal.Deserialize()
log.Logf(4, "new input from %v for syscall %v (signal=%v, cover=%v)",
a.Name, a.Call, inputSignal.Len(), len(a.Cover))
- if _, err := serv.target.Deserialize(a.RPCInput.Prog, prog.NonStrict); err != nil {
- // This should not happen, but we see such cases episodically, reason unknown.
+ p, err := serv.target.Deserialize(a.RPCInput.Prog, prog.NonStrict)
+ if err != nil {
+ // This should not happen, but we see such cases episodically (probably corrupted VM memory).
log.Logf(0, "failed to deserialize program from fuzzer: %v\n%s", err, a.RPCInput.Prog)
return nil
}
+ if len(p.Calls) > prog.MaxCalls {
+ log.Logf(0, "rejecting too long program from fuzzer: %v calls\n%s", len(p.Calls), a.RPCInput.Prog)
+ return nil
+ }
serv.mu.Lock()
defer serv.mu.Unlock()