forked from google/syzkaller
-
Notifications
You must be signed in to change notification settings - Fork 0
/
workqueue.go
131 lines (117 loc) · 3.35 KB
/
workqueue.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// 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"
"github.com/google/syzkaller/pkg/ipc"
"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{}
}
type ProgTypes int
const (
ProgCandidate ProgTypes = 1 << iota
ProgMinimized
ProgSmashed
ProgNormal ProgTypes = 0
)
// 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 {
p *prog.Prog
call int
info ipc.CallInfo
flags ProgTypes
}
// 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 {
p *prog.Prog
flags ProgTypes
}
// 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:
if item.flags&ProgCandidate != 0 {
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
}