diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2020-05-12 15:26:07 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2020-05-12 15:26:07 +0200 |
| commit | a44eb8f7eadcc16e73055948131b2c56b4fd5272 (patch) | |
| tree | fd56018398be21abc08d3248db0c0d6509151f04 | |
| parent | 9cea41ad339ff55a0eb50b43b15c73245fa86343 (diff) | |
syz-manager: more consistently check disabled syscalls
We have program "validity" check duplicated 4 times
(initially it was just "does it deserialize?").
Then we added program length and disabled syscall.
But some of the sites have only a subset of checks.
Factor out program checking procedure into a separate function
and use it at all sites.
| -rw-r--r-- | syz-manager/hub.go | 19 | ||||
| -rw-r--r-- | syz-manager/manager.go | 39 | ||||
| -rw-r--r-- | syz-manager/rpc.go | 17 |
3 files changed, 36 insertions, 39 deletions
diff --git a/syz-manager/hub.go b/syz-manager/hub.go index 51937b536..9028f0233 100644 --- a/syz-manager/hub.go +++ b/syz-manager/hub.go @@ -21,7 +21,7 @@ func (mgr *Manager) hubSyncLoop() { cfg: mgr.cfg, target: mgr.target, stats: mgr.stats, - enabledCalls: mgr.checkResult.EnabledCalls[mgr.cfg.Sandbox], + enabledCalls: mgr.targetEnabledSyscalls, leak: mgr.checkResult.Features[host.FeatureLeak].Enabled, fresh: mgr.fresh, hubReproQueue: mgr.hubReproQueue, @@ -37,7 +37,7 @@ type HubConnector struct { cfg *mgrconfig.Config target *prog.Target stats *Stats - enabledCalls []int + enabledCalls map[*prog.Syscall]bool leak bool fresh bool hubCorpus map[hash.Sig]bool @@ -80,8 +80,8 @@ func (hc *HubConnector) connect(corpus [][]byte) (*rpctype.RPCClient, error) { Manager: hc.cfg.Name, Fresh: hc.fresh, } - for _, id := range hc.enabledCalls { - a.Calls = append(a.Calls, hc.target.Syscalls[id].Name) + for call := range hc.enabledCalls { + a.Calls = append(a.Calls, call.Name) } hubCorpus := make(map[hash.Sig]bool) for _, inp := range corpus { @@ -171,8 +171,10 @@ func (hc *HubConnector) processProgs(progs [][]byte) int { dropped := 0 candidates := make([][]byte, 0, len(progs)) for _, inp := range progs { - p, err := hc.target.Deserialize(inp, prog.NonStrict) - if err != nil || len(p.Calls) > prog.MaxCalls { + bad, disabled := checkProgram(hc.target, hc.enabledCalls, inp) + if bad || disabled { + log.Logf(0, "rejecting program from hub (bad=%v, disabled=%v):\n%s", + bad, disabled, inp) dropped++ continue } @@ -185,7 +187,10 @@ func (hc *HubConnector) processProgs(progs [][]byte) int { func (hc *HubConnector) processRepros(repros [][]byte) int { dropped := 0 for _, repro := range repros { - if _, err := hc.target.Deserialize(repro, prog.NonStrict); err != nil { + bad, disabled := checkProgram(hc.target, hc.enabledCalls, repro) + if bad || disabled { + log.Logf(0, "rejecting repro from hub (bad=%v, disabled=%v):\n%s", + bad, disabled, repro) dropped++ continue } diff --git a/syz-manager/manager.go b/syz-manager/manager.go index ab1179f95..3e44d24e2 100644 --- a/syz-manager/manager.go +++ b/syz-manager/manager.go @@ -470,27 +470,14 @@ func (mgr *Manager) loadCorpus() { fallthrough case currentDBVersion: } - broken, tooLong := 0, 0 + broken := 0 for key, rec := range mgr.corpusDB.Records { - p, err := mgr.target.Deserialize(rec.Val, prog.NonStrict) - if err != nil { + bad, disabled := checkProgram(mgr.target, mgr.targetEnabledSyscalls, rec.Val) + if bad { mgr.corpusDB.Delete(key) broken++ continue } - if len(p.Calls) > prog.MaxCalls { - mgr.corpusDB.Delete(key) - tooLong++ - continue - } - - disabled := false - for _, c := range p.Calls { - if !mgr.targetEnabledSyscalls[c.Meta] { - disabled = true - break - } - } if disabled { // This program contains a disabled syscall. // We won't execute it, but remember its hash so @@ -505,8 +492,8 @@ func (mgr *Manager) loadCorpus() { }) } mgr.fresh = len(mgr.corpusDB.Records) == 0 - log.Logf(0, "%-24v: %v (deleted %v broken, %v too long)", - "corpus", len(mgr.candidates), broken, tooLong) + log.Logf(0, "%-24v: %v (deleted %v broken)", + "corpus", len(mgr.candidates), broken) // Now this is ugly. // We duplicate all inputs in the corpus and shuffle the second part. @@ -525,6 +512,22 @@ func (mgr *Manager) loadCorpus() { mgr.phase = phaseLoadedCorpus } +func checkProgram(target *prog.Target, enabled map[*prog.Syscall]bool, data []byte) (bad, disabled bool) { + p, err := target.Deserialize(data, prog.NonStrict) + if err != nil { + return true, true + } + if len(p.Calls) > prog.MaxCalls { + return true, true + } + for _, c := range p.Calls { + if !enabled[c.Meta] { + return false, true + } + } + return false, false +} + func (mgr *Manager) runInstance(index int) (*Crash, error) { mgr.checkUsedFiles() inst, err := mgr.vmPool.Create(index) diff --git a/syz-manager/rpc.go b/syz-manager/rpc.go index d734b8da9..6cf979a38 100644 --- a/syz-manager/rpc.go +++ b/syz-manager/rpc.go @@ -211,22 +211,11 @@ 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)) - 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) + bad, disabled := checkProgram(serv.target, serv.targetEnabledSyscalls, a.RPCInput.Prog) + if bad || disabled { + log.Logf(0, "rejecting program from fuzzer (bad=%v, disabled=%v):\n%s", bad, disabled, a.RPCInput.Prog) return nil } - for _, call := range p.Calls { - if !serv.targetEnabledSyscalls[call.Meta] { - log.Logf(0, "rejecting program with disabled call %v:\n%s", call.Meta.Name, a.RPCInput.Prog) - return nil - } - } serv.mu.Lock() defer serv.mu.Unlock() |
