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
|
// Copyright 2025 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 (
"bytes"
"context"
"flag"
"fmt"
"io"
"os"
"github.com/google/syzkaller/pkg/debugtracer"
"github.com/google/syzkaller/pkg/instance"
"github.com/google/syzkaller/pkg/mgrconfig"
"github.com/google/syzkaller/pkg/osutil"
"github.com/google/syzkaller/pkg/report"
"github.com/google/syzkaller/syz-cluster/pkg/api"
"github.com/google/syzkaller/syz-cluster/pkg/app"
"github.com/google/syzkaller/syz-cluster/pkg/fuzzconfig"
)
var (
flagConfig = flag.String("config", "", "syzkaller config")
flagSession = flag.String("session", "", "session ID")
flagTestName = flag.String("test_name", "", "test name")
flagBaseBuild = flag.String("base_build", "", "base build ID")
flagPatchedBuild = flag.String("patched_build", "", "patched build ID")
flagOutput = flag.String("output", "", "where to store the result")
flagFindings = flag.Bool("findings", false, "report failur as findings")
)
func main() {
flag.Parse()
if *flagConfig == "" || *flagSession == "" || *flagTestName == "" {
app.Fatalf("--config, --session and --test_name must be set")
}
ctx := context.Background()
client := app.DefaultClient()
testResult := &api.TestResult{
SessionID: *flagSession,
TestName: *flagTestName,
BaseBuildID: *flagBaseBuild,
PatchedBuildID: *flagPatchedBuild,
Result: api.TestRunning,
}
// Report that we've begun the test -- it will let us report the findings.
err := client.UploadTestResult(ctx, testResult)
if err != nil {
app.Fatalf("failed to upload test result: %v", err)
}
output := new(bytes.Buffer)
tracer := &debugtracer.GenericTracer{
WithTime: true,
TraceWriter: io.MultiWriter(os.Stderr, output),
}
bootedFine, err := runTest(ctx, client, tracer)
if err != nil {
app.Fatalf("failed to run the boot test: %v", err)
}
testResult.Log = output.Bytes()
if bootedFine {
testResult.Result = api.TestPassed
} else {
testResult.Result = api.TestFailed
}
// Report the test results.
err = client.UploadTestResult(ctx, testResult)
if err != nil {
app.Fatalf("failed to upload test result: %v", err)
}
if *flagOutput != "" {
osutil.WriteJSON(*flagOutput, &api.BootResult{
Success: bootedFine,
})
}
}
// To prevent false positive results, demand that in order to be marked as FAILED,
// the test must fail 3 times in a row.
const retryCount = 3
// The base config may have more VMs, but we don't need that many.
const vmCount = 3
func runTest(ctx context.Context, client *api.Client, tracer debugtracer.DebugTracer) (bool, error) {
cfg, err := fuzzconfig.GenerateBase(&api.FuzzConfig{})
if err != nil {
return false, err
}
if err := instance.OverrideVMCount(cfg, vmCount); err != nil {
return false, err
}
cfg.Workdir = "/tmp/test-workdir"
if err := mgrconfig.Complete(cfg); err != nil {
return false, fmt.Errorf("failed to complete the config: %w", err)
}
var rep *report.Report
for i := 0; i < retryCount; i++ {
tracer.Log("starting attempt #%d", i)
var err error
rep, err = instance.RunSmokeTest(cfg)
if err != nil {
return false, err
} else if rep == nil {
return true, nil
}
tracer.Log("attempt failed: %q", rep.Title)
}
if *flagFindings {
tracer.Log("reporting the finding")
findingErr := client.UploadFinding(ctx, &api.NewFinding{
SessionID: *flagSession,
TestName: *flagTestName,
Title: rep.Title,
Report: rep.Report,
Log: rep.Output,
})
if findingErr != nil {
return false, fmt.Errorf("failed to report the finding: %w", findingErr)
}
} else {
tracer.Log("report:\n%s", rep.Report)
tracer.Log("output:\n%s", rep.Output)
}
return false, nil
}
|