github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/tools/syz-testbuild/testbuild.go (about)

     1  // Copyright 2019 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  // syz-testbuild tests kernel build/boot on releases as it will be done by pkg/bisect.
     5  // This allows to ensure that, for example, a change to kernel config won't break
     6  // build/boot on older releases and consequently won't break bisection process.
     7  // The binary needs to run under root because it creates images.
     8  // The kernel checkout given to the tool will be cleaned and used for in-tree builds.
     9  // Example invocation:
    10  //
    11  //	sudo syz-testbuild -kernel_src $LINUX_CHECKOUT \
    12  //		-config dashboard/config/upstream-kasan.config \
    13  //		-sysctl dashboard/config/upstream.sysctl \
    14  //		-cmdline dashboard/config/upstream-apparmor.cmdline \
    15  //		-userspace $WHEEZY_USERSPACE \
    16  //		-bisect_bin $BISECT_BIN
    17  //
    18  // A suitable wheezy userspace can be downloaded from:
    19  // https://storage.googleapis.com/syzkaller/wheezy.tar.gz
    20  // A set of binaries required for bisection (older compilers) can be downloaded from:
    21  // https://storage.googleapis.com/syzkaller/bisect_bin.tar.gz
    22  package main
    23  
    24  import (
    25  	"encoding/json"
    26  	"errors"
    27  	"flag"
    28  	"fmt"
    29  	"log"
    30  	"os"
    31  	"runtime"
    32  
    33  	"github.com/google/syzkaller/pkg/build"
    34  	"github.com/google/syzkaller/pkg/instance"
    35  	"github.com/google/syzkaller/pkg/mgrconfig"
    36  	"github.com/google/syzkaller/pkg/osutil"
    37  	"github.com/google/syzkaller/pkg/tool"
    38  	"github.com/google/syzkaller/pkg/vcs"
    39  )
    40  
    41  var (
    42  	flagOS            = flag.String("os", runtime.GOOS, "OS to test")
    43  	flagArch          = flag.String("arch", runtime.GOARCH, "arch to test")
    44  	flagKernelSrc     = flag.String("kernel_src", "", "path to kernel checkout")
    45  	flagKernelConfig  = flag.String("config", "", "kernel config")
    46  	flagKernelSysctl  = flag.String("sysctl", "", "kernel sysctl file")
    47  	flagKernelCmdline = flag.String("cmdline", "", "kernel cmdline file")
    48  	flagUserspace     = flag.String("userspace", "", "path to userspace for build")
    49  	flagBisectBin     = flag.String("bisect_bin", "", "path to bisection binaries")
    50  	flagSyzkaller     = flag.String("syzkaller", ".", "path to built syzkaller")
    51  	flagSandbox       = flag.String("sandbox", "namespace", "sandbox to use for testing")
    52  	flagSandboxArg    = flag.Int("sandbox_arg", 0, "an argument for sandbox runner")
    53  )
    54  
    55  const (
    56  	vmType   = "qemu"
    57  	numTests = 5
    58  )
    59  
    60  func main() {
    61  	flag.Parse()
    62  	if os.Getuid() != 0 {
    63  		tool.Failf("image build will fail, run under root")
    64  	}
    65  	os.Setenv("SYZ_DISABLE_SANDBOXING", "yes")
    66  	dir, err := os.MkdirTemp("", "syz-testbuild")
    67  	if err != nil {
    68  		tool.Fail(err)
    69  	}
    70  	defer os.RemoveAll(dir)
    71  	cfg := &mgrconfig.Config{
    72  		RawTarget:  *flagOS + "/" + *flagArch,
    73  		HTTP:       ":0",
    74  		Workdir:    dir,
    75  		KernelSrc:  *flagKernelSrc,
    76  		KernelObj:  *flagKernelSrc,
    77  		Syzkaller:  *flagSyzkaller,
    78  		Sandbox:    *flagSandbox,
    79  		SandboxArg: *flagSandboxArg,
    80  		SSHUser:    "root",
    81  		Procs:      1,
    82  		Cover:      false,
    83  		Type:       vmType,
    84  		VM:         json.RawMessage([]byte(fmt.Sprintf(`{ "count": %v, "cpu": 2, "mem": 2048 }`, numTests))),
    85  		Derived: mgrconfig.Derived{
    86  			TargetOS:     *flagOS,
    87  			TargetArch:   *flagArch,
    88  			TargetVMArch: *flagArch,
    89  		},
    90  	}
    91  	if err := mgrconfig.Complete(cfg); err != nil {
    92  		tool.Fail(err)
    93  	}
    94  	repo, err := vcs.NewRepo(*flagOS, vmType, *flagKernelSrc)
    95  	if err != nil {
    96  		tool.Fail(err)
    97  	}
    98  	bisecter := repo.(vcs.Bisecter)
    99  	head, err := repo.HeadCommit()
   100  	if err != nil {
   101  		tool.Fail(err)
   102  	}
   103  	log.Printf("HEAD is on %v %v", head.Hash, head.Title)
   104  	tags, err := bisecter.PreviousReleaseTags(head.Hash, "gcc")
   105  	if err != nil {
   106  		tool.Fail(err)
   107  	}
   108  	log.Printf("tags: %v", tags)
   109  	kernelConfig, err := os.ReadFile(*flagKernelConfig)
   110  	if err != nil {
   111  		tool.Fail(err)
   112  	}
   113  	env, err := instance.NewEnv(cfg, nil, nil)
   114  	if err != nil {
   115  		tool.Fail(err)
   116  	}
   117  	test(repo, bisecter, kernelConfig, env, head)
   118  	for _, tag := range tags {
   119  		com, err := repo.SwitchCommit(tag)
   120  		if err != nil {
   121  			tool.Fail(err)
   122  		}
   123  		test(repo, bisecter, kernelConfig, env, com)
   124  	}
   125  }
   126  
   127  func test(repo vcs.Repo, bisecter vcs.Bisecter, kernelConfig []byte, env instance.Env, com *vcs.Commit) {
   128  	compiler, compilerType, linker, ccache := "gcc", "gcc", "ld", ""
   129  	bisectEnv, err := bisecter.EnvForCommit(compiler, compilerType, *flagBisectBin, com.Hash, kernelConfig, nil)
   130  	if err != nil {
   131  		tool.Fail(err)
   132  	}
   133  	log.Printf("testing: %v %v using %v", com.Hash, com.Title, bisectEnv.Compiler)
   134  	if err := build.Clean(*flagOS, *flagArch, vmType, *flagKernelSrc); err != nil {
   135  		tool.Fail(err)
   136  	}
   137  	_, _, err = env.BuildKernel(&instance.BuildKernelConfig{
   138  		CompilerBin:  bisectEnv.Compiler,
   139  		LinkerBin:    linker,
   140  		CcacheBin:    ccache,
   141  		UserspaceDir: *flagUserspace,
   142  		CmdlineFile:  *flagKernelCmdline,
   143  		SysctlFile:   *flagKernelSysctl,
   144  		KernelConfig: bisectEnv.KernelConfig,
   145  	})
   146  	if err != nil {
   147  		var verr *osutil.VerboseError
   148  		if errors.As(err, &verr) {
   149  			log.Printf("BUILD BROKEN: %v", verr.Title)
   150  			saveLog(com.Hash, 0, verr.Output)
   151  		} else {
   152  			log.Printf("BUILD BROKEN: %v", err)
   153  		}
   154  		return
   155  	}
   156  	log.Printf("build OK")
   157  	results, err := env.Test(numTests, nil, nil, nil)
   158  	if err != nil {
   159  		tool.Fail(err)
   160  	}
   161  	var verdicts []string
   162  	for i, res := range results {
   163  		if res.Error == nil {
   164  			verdicts = append(verdicts, "OK")
   165  			continue
   166  		}
   167  
   168  		var testError *instance.TestError
   169  		var crashError *instance.CrashError
   170  		switch {
   171  		case errors.As(res.Error, &testError):
   172  			if testError.Boot {
   173  				verdicts = append(verdicts, fmt.Sprintf("boot failed: %v", testError))
   174  			} else {
   175  				verdicts = append(verdicts, fmt.Sprintf("basic kernel testing failed: %v", testError))
   176  			}
   177  			output := testError.Output
   178  			if testError.Report != nil {
   179  				output = testError.Report.Output
   180  			}
   181  			saveLog(com.Hash, i, output)
   182  		case errors.As(res.Error, &crashError):
   183  			verdicts = append(verdicts, fmt.Sprintf("crashed: %v", crashError))
   184  			output := crashError.Report.Report
   185  			if len(output) == 0 {
   186  				output = crashError.Report.Output
   187  			}
   188  			saveLog(com.Hash, i, output)
   189  		default:
   190  			verdicts = append(verdicts, fmt.Sprintf("failed: %v", err))
   191  		}
   192  	}
   193  	unique := make(map[string]bool)
   194  	for _, verdict := range verdicts {
   195  		unique[verdict] = true
   196  	}
   197  	if len(unique) == 1 {
   198  		log.Printf("all runs: %v", verdicts[0])
   199  	} else {
   200  		for i, verdict := range verdicts {
   201  			log.Printf("run #%v: %v", i, verdict)
   202  		}
   203  	}
   204  }
   205  
   206  func saveLog(hash string, idx int, data []byte) {
   207  	if len(data) == 0 {
   208  		return
   209  	}
   210  	osutil.WriteFile(fmt.Sprintf("%v.%v", hash, idx), data)
   211  }