2017-12-17 17:34:17 +00:00
|
|
|
// Copyright 2017 syzkaller project authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sync"
|
|
|
|
|
2018-02-18 11:09:37 +00:00
|
|
|
"github.com/google/syzkaller/pkg/ipc"
|
2017-12-17 17:34:17 +00:00
|
|
|
"github.com/google/syzkaller/prog"
|
|
|
|
)
|
|
|
|
|
|
|
|
// WorkQueue holds global non-fuzzing work items (see the Work* structs below).
|
|
|
|
// WorkQueue also does prioritization among work items, for example, we want
|
|
|
|
// to triage and send to manager new inputs before we smash programs
|
|
|
|
// in order to not permanently lose interesting programs in case of VM crash.
|
|
|
|
type WorkQueue struct {
|
|
|
|
mu sync.RWMutex
|
|
|
|
triageCandidate []*WorkTriage
|
|
|
|
candidate []*WorkCandidate
|
|
|
|
triage []*WorkTriage
|
|
|
|
smash []*WorkSmash
|
|
|
|
|
|
|
|
procs int
|
|
|
|
needCandidates chan struct{}
|
|
|
|
}
|
|
|
|
|
2018-02-18 09:49:16 +00:00
|
|
|
type ProgTypes int
|
|
|
|
|
|
|
|
const (
|
|
|
|
ProgCandidate ProgTypes = 1 << iota
|
|
|
|
ProgMinimized
|
|
|
|
ProgSmashed
|
|
|
|
ProgNormal ProgTypes = 0
|
|
|
|
)
|
|
|
|
|
2017-12-17 17:34:17 +00:00
|
|
|
// WorkTriage are programs for which we noticed potential new coverage during
|
|
|
|
// first execution. But we are not sure yet if the coverage is real or not.
|
|
|
|
// During triage we understand if these programs in fact give new coverage,
|
|
|
|
// and if yes, minimize them and add to corpus.
|
|
|
|
type WorkTriage struct {
|
2018-02-18 11:09:37 +00:00
|
|
|
p *prog.Prog
|
|
|
|
call int
|
|
|
|
info ipc.CallInfo
|
|
|
|
flags ProgTypes
|
2017-12-17 17:34:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// WorkCandidate are programs from hub.
|
|
|
|
// We don't know yet if they are useful for this fuzzer or not.
|
|
|
|
// A proc handles them the same way as locally generated/mutated programs.
|
|
|
|
type WorkCandidate struct {
|
2018-02-18 09:49:16 +00:00
|
|
|
p *prog.Prog
|
|
|
|
flags ProgTypes
|
2017-12-17 17:34:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// WorkSmash are programs just added to corpus.
|
|
|
|
// During smashing these programs receive a one-time special attention
|
|
|
|
// (emit faults, collect comparison hints, etc).
|
|
|
|
type WorkSmash struct {
|
|
|
|
p *prog.Prog
|
|
|
|
call int
|
|
|
|
}
|
|
|
|
|
|
|
|
func newWorkQueue(procs int, needCandidates chan struct{}) *WorkQueue {
|
|
|
|
return &WorkQueue{
|
|
|
|
procs: procs,
|
|
|
|
needCandidates: needCandidates,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (wq *WorkQueue) enqueue(item interface{}) {
|
|
|
|
wq.mu.Lock()
|
|
|
|
defer wq.mu.Unlock()
|
|
|
|
switch item := item.(type) {
|
|
|
|
case *WorkTriage:
|
2018-02-18 09:49:16 +00:00
|
|
|
if item.flags&ProgCandidate != 0 {
|
2017-12-17 17:34:17 +00:00
|
|
|
wq.triageCandidate = append(wq.triageCandidate, item)
|
|
|
|
} else {
|
|
|
|
wq.triage = append(wq.triage, item)
|
|
|
|
}
|
|
|
|
case *WorkCandidate:
|
|
|
|
wq.candidate = append(wq.candidate, item)
|
|
|
|
case *WorkSmash:
|
|
|
|
wq.smash = append(wq.smash, item)
|
|
|
|
default:
|
|
|
|
panic("unknown work type")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (wq *WorkQueue) dequeue() (item interface{}) {
|
|
|
|
wq.mu.RLock()
|
|
|
|
if len(wq.triageCandidate)+len(wq.candidate)+len(wq.triage)+len(wq.smash) == 0 {
|
|
|
|
wq.mu.RUnlock()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
wq.mu.RUnlock()
|
|
|
|
wq.mu.Lock()
|
|
|
|
wantCandidates := false
|
|
|
|
if len(wq.triageCandidate) != 0 {
|
|
|
|
last := len(wq.triageCandidate) - 1
|
|
|
|
item = wq.triageCandidate[last]
|
|
|
|
wq.triageCandidate = wq.triageCandidate[:last]
|
|
|
|
} else if len(wq.candidate) != 0 {
|
|
|
|
last := len(wq.candidate) - 1
|
|
|
|
item = wq.candidate[last]
|
|
|
|
wq.candidate = wq.candidate[:last]
|
|
|
|
wantCandidates = len(wq.candidate) < wq.procs
|
|
|
|
} else if len(wq.triage) != 0 {
|
|
|
|
last := len(wq.triage) - 1
|
|
|
|
item = wq.triage[last]
|
|
|
|
wq.triage = wq.triage[:last]
|
|
|
|
} else if len(wq.smash) != 0 {
|
|
|
|
last := len(wq.smash) - 1
|
|
|
|
item = wq.smash[last]
|
|
|
|
wq.smash = wq.smash[:last]
|
|
|
|
}
|
|
|
|
wq.mu.Unlock()
|
|
|
|
if wantCandidates {
|
|
|
|
select {
|
|
|
|
case wq.needCandidates <- struct{}{}:
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return item
|
|
|
|
}
|
|
|
|
|
|
|
|
func (wq *WorkQueue) wantCandidates() bool {
|
|
|
|
wq.mu.RLock()
|
|
|
|
defer wq.mu.RUnlock()
|
|
|
|
return len(wq.candidate) < wq.procs
|
|
|
|
}
|