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  }