github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/syz-cluster/workflow/boot-step/main.go (about) 1 // Copyright 2025 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package main 5 6 import ( 7 "bytes" 8 "context" 9 "flag" 10 "fmt" 11 "io" 12 "os" 13 14 "github.com/google/syzkaller/pkg/debugtracer" 15 "github.com/google/syzkaller/pkg/instance" 16 "github.com/google/syzkaller/pkg/mgrconfig" 17 "github.com/google/syzkaller/pkg/osutil" 18 "github.com/google/syzkaller/pkg/report" 19 "github.com/google/syzkaller/syz-cluster/pkg/api" 20 "github.com/google/syzkaller/syz-cluster/pkg/app" 21 "github.com/google/syzkaller/syz-cluster/pkg/fuzzconfig" 22 ) 23 24 var ( 25 flagConfig = flag.String("config", "", "syzkaller config") 26 flagSession = flag.String("session", "", "session ID") 27 flagTestName = flag.String("test_name", "", "test name") 28 flagBaseBuild = flag.String("base_build", "", "base build ID") 29 flagPatchedBuild = flag.String("patched_build", "", "patched build ID") 30 flagOutput = flag.String("output", "", "where to store the result") 31 flagFindings = flag.Bool("findings", false, "report failur as findings") 32 ) 33 34 func main() { 35 flag.Parse() 36 if *flagConfig == "" || *flagSession == "" || *flagTestName == "" { 37 app.Fatalf("--config, --session and --test_name must be set") 38 } 39 40 ctx := context.Background() 41 client := app.DefaultClient() 42 43 testResult := &api.TestResult{ 44 SessionID: *flagSession, 45 TestName: *flagTestName, 46 BaseBuildID: *flagBaseBuild, 47 PatchedBuildID: *flagPatchedBuild, 48 Result: api.TestRunning, 49 } 50 // Report that we've begun the test -- it will let us report the findings. 51 err := client.UploadTestResult(ctx, testResult) 52 if err != nil { 53 app.Fatalf("failed to upload test result: %v", err) 54 } 55 56 output := new(bytes.Buffer) 57 tracer := &debugtracer.GenericTracer{ 58 WithTime: true, 59 TraceWriter: io.MultiWriter(os.Stderr, output), 60 } 61 bootedFine, err := runTest(ctx, client, tracer) 62 if err != nil { 63 app.Fatalf("failed to run the boot test: %v", err) 64 } 65 testResult.Log = output.Bytes() 66 if bootedFine { 67 testResult.Result = api.TestPassed 68 } else { 69 testResult.Result = api.TestFailed 70 } 71 72 // Report the test results. 73 err = client.UploadTestResult(ctx, testResult) 74 if err != nil { 75 app.Fatalf("failed to upload test result: %v", err) 76 } 77 if *flagOutput != "" { 78 osutil.WriteJSON(*flagOutput, &api.BootResult{ 79 Success: bootedFine, 80 }) 81 } 82 } 83 84 // To prevent false positive results, demand that in order to be marked as FAILED, 85 // the test must fail 3 times in a row. 86 const retryCount = 3 87 88 // The base config may have more VMs, but we don't need that many. 89 const vmCount = 3 90 91 func runTest(ctx context.Context, client *api.Client, tracer debugtracer.DebugTracer) (bool, error) { 92 cfg, err := fuzzconfig.GenerateBase(&api.FuzzConfig{}) 93 if err != nil { 94 return false, err 95 } 96 if err := instance.OverrideVMCount(cfg, vmCount); err != nil { 97 return false, err 98 } 99 cfg.Workdir = "/tmp/test-workdir" 100 if err := mgrconfig.Complete(cfg); err != nil { 101 return false, fmt.Errorf("failed to complete the config: %w", err) 102 } 103 104 var rep *report.Report 105 for i := 0; i < retryCount; i++ { 106 tracer.Log("starting attempt #%d", i) 107 var err error 108 rep, err = instance.RunSmokeTest(cfg) 109 if err != nil { 110 return false, err 111 } else if rep == nil { 112 return true, nil 113 } 114 tracer.Log("attempt failed: %q", rep.Title) 115 } 116 if *flagFindings { 117 tracer.Log("reporting the finding") 118 findingErr := client.UploadFinding(ctx, &api.NewFinding{ 119 SessionID: *flagSession, 120 TestName: *flagTestName, 121 Title: rep.Title, 122 Report: rep.Report, 123 Log: rep.Output, 124 }) 125 if findingErr != nil { 126 return false, fmt.Errorf("failed to report the finding: %w", findingErr) 127 } 128 } else { 129 tracer.Log("report:\n%s", rep.Report) 130 tracer.Log("output:\n%s", rep.Output) 131 } 132 return false, nil 133 }