forked from google/syzkaller
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsubsystem.go
206 lines (191 loc) · 6.04 KB
/
subsystem.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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// Copyright 2023 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 (
"context"
"fmt"
"reflect"
"sort"
"time"
"github.com/google/syzkaller/pkg/debugtracer"
"github.com/google/syzkaller/pkg/subsystem"
db "google.golang.org/appengine/v2/datastore"
"google.golang.org/appengine/v2/log"
)
// reassignBugSubsystems is expected to be periodically called to refresh old automatic
// subsystem assignments.
func reassignBugSubsystems(c context.Context, ns string, count int) error {
service := getNsConfig(c, ns).Subsystems.Service
if service == nil {
return nil
}
bugs, keys, err := bugsToUpdateSubsystems(c, ns, count)
if err != nil {
return err
}
log.Infof(c, "updating subsystems for %d bugs in %#v", len(keys), ns)
rev := getNsConfig(c, ns).Subsystems.Revision
for i, bugKey := range keys {
if bugs[i].hasUserSubsystems() {
// It might be that the user-set subsystem no longer exists.
// For now let's just log an error in this case.
checkOutdatedSubsystems(c, service, bugs[i])
// If we don't set the latst revision, we'll have to update this
// bug over and over again.
err = updateBugSubsystems(c, bugKey, nil, updateRevision(rev))
} else {
var list []*subsystem.Subsystem
list, err = inferSubsystems(c, bugs[i], bugKey, &debugtracer.NullTracer{})
if err != nil {
return fmt.Errorf("failed to infer subsystems: %w", err)
}
err = updateBugSubsystems(c, bugKey, list, autoInference(rev))
}
if err != nil {
return fmt.Errorf("failed to save subsystems: %w", err)
}
}
return nil
}
func bugsToUpdateSubsystems(c context.Context, ns string, count int) ([]*Bug, []*db.Key, error) {
now := timeNow(c)
rev := getSubsystemRevision(c, ns)
queries := []*db.Query{
// If revision has been updated, first update open bugs.
db.NewQuery("Bug").
Filter("Namespace=", ns).
Filter("Status=", BugStatusOpen).
Filter("SubsystemsRev<", rev),
// The next priority is the regular update of open bugs.
db.NewQuery("Bug").
Filter("Namespace=", ns).
Filter("Status=", BugStatusOpen).
Filter("SubsystemsTime<", now.Add(-openBugsUpdateTime)),
// Then let's consider the update of closed bugs.
db.NewQuery("Bug").
Filter("Namespace=", ns).
Filter("Status=", BugStatusFixed).
Filter("SubsystemsRev<", rev),
// And, at the end, everything else.
db.NewQuery("Bug").
Filter("Namespace=", ns).
Filter("SubsystemsRev<", rev),
}
var bugs []*Bug
var keys []*db.Key
for i, query := range queries {
if count <= 0 {
break
}
var tmpBugs []*Bug
tmpKeys, err := query.Limit(count).GetAll(c, &tmpBugs)
if err != nil {
return nil, nil, fmt.Errorf("query %d failed: %w", i, err)
}
bugs = append(bugs, tmpBugs...)
keys = append(keys, tmpKeys...)
count -= len(tmpKeys)
}
return bugs, keys, nil
}
func checkOutdatedSubsystems(c context.Context, service *subsystem.Service, bug *Bug) {
for _, item := range bug.LabelValues(SubsystemLabel) {
if service.ByName(item.Value) == nil {
log.Errorf(c, "ns=%s bug=%s subsystem %s no longer exists", bug.Namespace, bug.Title, item.Value)
}
}
}
type (
autoInference int
updateRevision int
)
func updateBugSubsystems(c context.Context, bugKey *db.Key,
list []*subsystem.Subsystem, info interface{}) error {
now := timeNow(c)
return updateSingleBug(c, bugKey, func(bug *Bug) error {
switch v := info.(type) {
case autoInference:
logSubsystemChange(c, bug, list)
bug.SetAutoSubsystems(c, list, now, int(v))
case updateRevision:
bug.SubsystemsRev = int(v)
bug.SubsystemsTime = now
}
return nil
})
}
func logSubsystemChange(c context.Context, bug *Bug, new []*subsystem.Subsystem) {
var oldNames, newNames []string
for _, item := range bug.LabelValues(SubsystemLabel) {
oldNames = append(oldNames, item.Value)
}
for _, item := range new {
newNames = append(newNames, item.Name)
}
sort.Strings(oldNames)
sort.Strings(newNames)
if !reflect.DeepEqual(oldNames, newNames) {
log.Infof(c, "bug %s: subsystems set from %v to %v",
bug.keyHash(c), oldNames, newNames)
}
}
const (
// We load the top crashesForInference crashes to determine the bug subsystem(s).
crashesForInference = 7
// How often we update open bugs.
openBugsUpdateTime = time.Hour * 24 * 30
)
// inferSubsystems determines the best yet possible estimate of the bug's subsystems.
func inferSubsystems(c context.Context, bug *Bug, bugKey *db.Key,
tracer debugtracer.DebugTracer) ([]*subsystem.Subsystem, error) {
service := getSubsystemService(c, bug.Namespace)
if service == nil {
// There's nothing we can do.
return nil, nil
}
dbCrashes, dbCrashKeys, err := queryCrashesForBug(c, bugKey, crashesForInference)
if err != nil {
return nil, err
}
crashes := []*subsystem.Crash{}
for i, dbCrash := range dbCrashes {
crash := &subsystem.Crash{}
if len(dbCrash.ReportElements.GuiltyFiles) > 0 {
// For now we anyway only store one.
crash.GuiltyPath = dbCrash.ReportElements.GuiltyFiles[0]
}
if dbCrash.ReproSyz != 0 {
crash.SyzRepro, _, err = getText(c, textReproSyz, dbCrash.ReproSyz)
if err != nil {
return nil, fmt.Errorf("failed to load syz repro for %s: %w",
dbCrashKeys[i], err)
}
}
crashes = append(crashes, crash)
}
return service.TracedExtract(crashes, tracer), nil
}
// subsystemMaintainers queries the list of emails to send the bug to.
func subsystemMaintainers(c context.Context, ns, subsystemName string) []string {
service := getNsConfig(c, ns).Subsystems.Service
if service == nil {
return nil
}
item := service.ByName(subsystemName)
if item == nil {
return nil
}
return item.Emails()
}
func getSubsystemService(c context.Context, ns string) *subsystem.Service {
return getNsConfig(c, ns).Subsystems.Service
}
func getSubsystemRevision(c context.Context, ns string) int {
return getNsConfig(c, ns).Subsystems.Revision
}
func subsystemListURL(c context.Context, ns string) string {
if getNsConfig(c, ns).Subsystems.Service == nil {
return ""
}
return fmt.Sprintf("%v/%v/subsystems?all=true", appURL(c), ns)
}