syz-fuzzer: add more checks for disabled syscalls

We are seeing some panics that say that some disabled
syscalls somehow get into corpus.
I don't see where/how this can happen.
Add a check to syz-fuzzer to panic whenever we execute
a program with disabled syscall. Hopefull the panic
stack will shed some light.
Also add a check in manager as the last defence line
so that bad programs don't get into the corpus.
This commit is contained in:
Dmitry Vyukov 2020-05-07 15:34:43 +02:00
parent fa822db46a
commit 413b991c26
5 changed files with 67 additions and 58 deletions

View File

@ -254,7 +254,7 @@ func (target *Target) BuildChoiceTable(corpus []*Prog, enabled map[*Syscall]bool
return &ChoiceTable{target, run, enabledCalls}
}
func (ct *ChoiceTable) enabled(call int) bool {
func (ct *ChoiceTable) Enabled(call int) bool {
return ct.runs[call] != nil
}
@ -262,13 +262,13 @@ func (ct *ChoiceTable) choose(r *rand.Rand, bias int) int {
if bias < 0 {
bias = ct.calls[r.Intn(len(ct.calls))].ID
}
if !ct.enabled(bias) {
if !ct.Enabled(bias) {
panic("bias to disabled syscall")
}
run := ct.runs[bias]
x := r.Intn(run[len(run)-1]) + 1
res := sort.SearchInts(run, x)
if !ct.enabled(res) {
if !ct.Enabled(res) {
panic("selected disabled syscall")
}
return res

View File

@ -379,7 +379,7 @@ func (r *randGen) createResource(s *state, res *ResourceType, dir Dir) (arg Arg,
// TODO: reduce priority of less specialized ctors.
var metas []*Syscall
for _, meta := range metas0 {
if s.ct.enabled(meta.ID) {
if s.ct.Enabled(meta.ID) {
metas = append(metas, meta)
}
}

View File

@ -277,6 +277,11 @@ func (proc *Proc) executeRaw(opts *ipc.ExecOpts, p *prog.Prog, stat Stat) *ipc.P
if opts.Flags&ipc.FlagDedupCover == 0 {
log.Fatalf("dedup cover is not enabled")
}
for _, call := range p.Calls {
if !proc.fuzzer.choiceTable.Enabled(call.Meta.ID) {
panic(fmt.Sprintf("executing disabled syscall %v", call.Meta.Name))
}
}
// Limit concurrency window and do leak checking once in a while.
ticket := proc.fuzzer.gate.Enter()

View File

@ -65,9 +65,10 @@ type Manager struct {
dash *dashapi.Dashboard
mu sync.Mutex
phase int
enabledSyscalls []int
mu sync.Mutex
phase int
configEnabledSyscalls []int
targetEnabledSyscalls map[*prog.Syscall]bool
candidates []rpctype.RPCCandidate // untriaged inputs from corpus and hub
disabledHashes map[string]struct{}
@ -157,27 +158,27 @@ func RunManager(cfg *mgrconfig.Config, target *prog.Target, sysTarget *targets.T
}
mgr := &Manager{
cfg: cfg,
vmPool: vmPool,
target: target,
sysTarget: sysTarget,
reporter: reporter,
crashdir: crashdir,
startTime: time.Now(),
stats: &Stats{haveHub: cfg.HubClient != ""},
crashTypes: make(map[string]bool),
enabledSyscalls: syscalls,
corpus: make(map[string]rpctype.RPCInput),
disabledHashes: make(map[string]struct{}),
memoryLeakFrames: make(map[string]bool),
dataRaceFrames: make(map[string]bool),
fresh: true,
vmStop: make(chan bool),
hubReproQueue: make(chan *Crash, 10),
needMoreRepros: make(chan chan bool),
reproRequest: make(chan chan map[string]bool),
usedFiles: make(map[string]time.Time),
saturatedCalls: make(map[string]bool),
cfg: cfg,
vmPool: vmPool,
target: target,
sysTarget: sysTarget,
reporter: reporter,
crashdir: crashdir,
startTime: time.Now(),
stats: &Stats{haveHub: cfg.HubClient != ""},
crashTypes: make(map[string]bool),
configEnabledSyscalls: syscalls,
corpus: make(map[string]rpctype.RPCInput),
disabledHashes: make(map[string]struct{}),
memoryLeakFrames: make(map[string]bool),
dataRaceFrames: make(map[string]bool),
fresh: true,
vmStop: make(chan bool),
hubReproQueue: make(chan *Crash, 10),
needMoreRepros: make(chan chan bool),
reproRequest: make(chan chan map[string]bool),
usedFiles: make(map[string]time.Time),
saturatedCalls: make(map[string]bool),
}
log.Logf(0, "loading corpus...")
@ -469,10 +470,6 @@ func (mgr *Manager) loadCorpus() {
fallthrough
case currentDBVersion:
}
syscalls := make(map[int]bool)
for _, id := range mgr.checkResult.EnabledCalls[mgr.cfg.Sandbox] {
syscalls[id] = true
}
broken, tooLong := 0, 0
for key, rec := range mgr.corpusDB.Records {
p, err := mgr.target.Deserialize(rec.Val, prog.NonStrict)
@ -489,7 +486,7 @@ func (mgr *Manager) loadCorpus() {
disabled := false
for _, c := range p.Calls {
if !syscalls[c.Meta.ID] {
if !mgr.targetEnabledSyscalls[c.Meta] {
disabled = true
break
}
@ -1008,7 +1005,7 @@ func (mgr *Manager) fuzzerConnect() ([]rpctype.RPCInput, BugFrames) {
return corpus, BugFrames{memoryLeaks: memoryLeakFrames, dataRaces: dataRaceFrames}
}
func (mgr *Manager) machineChecked(a *rpctype.CheckArgs) {
func (mgr *Manager) machineChecked(a *rpctype.CheckArgs, enabledSyscalls map[*prog.Syscall]bool) {
mgr.mu.Lock()
defer mgr.mu.Unlock()
if len(mgr.cfg.EnabledSyscalls) != 0 && len(a.DisabledCalls[mgr.cfg.Sandbox]) != 0 {
@ -1016,7 +1013,7 @@ func (mgr *Manager) machineChecked(a *rpctype.CheckArgs) {
for _, dc := range a.DisabledCalls[mgr.cfg.Sandbox] {
disabled[mgr.target.Syscalls[dc.ID].Name] = dc.Reason
}
for _, id := range mgr.enabledSyscalls {
for _, id := range mgr.configEnabledSyscalls {
name := mgr.target.Syscalls[id].Name
if reason := disabled[name]; reason != "" {
log.Logf(0, "disabling %v: %v", name, reason)
@ -1027,12 +1024,12 @@ func (mgr *Manager) machineChecked(a *rpctype.CheckArgs) {
log.Fatalf("machine check: %v", a.Error)
}
log.Logf(0, "machine check:")
log.Logf(0, "%-24v: %v/%v", "syscalls",
len(a.EnabledCalls[mgr.cfg.Sandbox]), len(mgr.target.Syscalls))
log.Logf(0, "%-24v: %v/%v", "syscalls", len(enabledSyscalls), len(mgr.target.Syscalls))
for _, feat := range a.Features.Supported() {
log.Logf(0, "%-24v: %v", feat.Name, feat.Reason)
}
mgr.checkResult = a
mgr.targetEnabledSyscalls = enabledSyscalls
mgr.loadCorpus()
mgr.firstConnect = time.Now()
}

View File

@ -18,12 +18,13 @@ import (
)
type RPCServer struct {
mgr RPCManagerView
target *prog.Target
enabledSyscalls []int
stats *Stats
sandbox string
batchSize int
mgr RPCManagerView
target *prog.Target
configEnabledSyscalls []int
targetEnabledSyscalls map[*prog.Syscall]bool
stats *Stats
sandbox string
batchSize int
mu sync.Mutex
fuzzers map[string]*Fuzzer
@ -50,7 +51,7 @@ type BugFrames struct {
// RPCManagerView restricts interface between RPCServer and Manager.
type RPCManagerView interface {
fuzzerConnect() ([]rpctype.RPCInput, BugFrames)
machineChecked(result *rpctype.CheckArgs)
machineChecked(result *rpctype.CheckArgs, enabledSyscalls map[*prog.Syscall]bool)
newInput(inp rpctype.RPCInput, sign signal.Signal) bool
candidateBatch(size int) []rpctype.RPCCandidate
rotateCorpus() bool
@ -58,13 +59,13 @@ type RPCManagerView interface {
func startRPCServer(mgr *Manager) (int, error) {
serv := &RPCServer{
mgr: mgr,
target: mgr.target,
enabledSyscalls: mgr.enabledSyscalls,
stats: mgr.stats,
sandbox: mgr.cfg.Sandbox,
fuzzers: make(map[string]*Fuzzer),
rnd: rand.New(rand.NewSource(time.Now().UnixNano())),
mgr: mgr,
target: mgr.target,
configEnabledSyscalls: mgr.configEnabledSyscalls,
stats: mgr.stats,
sandbox: mgr.cfg.Sandbox,
fuzzers: make(map[string]*Fuzzer),
rnd: rand.New(rand.NewSource(time.Now().UnixNano())),
}
serv.batchSize = 5
if serv.batchSize < mgr.cfg.Procs {
@ -95,7 +96,7 @@ func (serv *RPCServer) Connect(a *rpctype.ConnectArgs, r *rpctype.ConnectRes) er
serv.fuzzers[a.Name] = f
r.MemoryLeakFrames = bugFrames.memoryLeaks
r.DataRaceFrames = bugFrames.dataRaces
r.EnabledCalls = serv.enabledSyscalls
r.EnabledCalls = serv.configEnabledSyscalls
r.GitRevision = prog.GitRevision
r.TargetRevision = serv.target.Revision
// TODO: temporary disabled b/c we suspect this negatively affects fuzzing.
@ -195,14 +196,14 @@ func (serv *RPCServer) Check(a *rpctype.CheckArgs, r *int) error {
if serv.checkResult != nil {
return nil
}
serv.mgr.machineChecked(a)
serv.targetEnabledSyscalls = make(map[*prog.Syscall]bool)
for _, call := range a.EnabledCalls[serv.sandbox] {
serv.targetEnabledSyscalls[serv.target.Syscalls[call]] = true
}
serv.mgr.machineChecked(a, serv.targetEnabledSyscalls)
a.DisabledCalls = nil
serv.checkResult = a
calls := make(map[*prog.Syscall]bool)
for _, call := range a.EnabledCalls[serv.sandbox] {
calls[serv.target.Syscalls[call]] = true
}
serv.rotator = prog.MakeRotator(serv.target, calls, serv.rnd)
serv.rotator = prog.MakeRotator(serv.target, serv.targetEnabledSyscalls, serv.rnd)
return nil
}
@ -220,6 +221,12 @@ func (serv *RPCServer) NewInput(a *rpctype.NewInputArgs, r *int) error {
log.Logf(0, "rejecting too long program from fuzzer: %v calls\n%s", len(p.Calls), 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()