github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/build/gvisor.go (about) 1 // Copyright 2018 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 build 5 6 import ( 7 "fmt" 8 "path/filepath" 9 "regexp" 10 "strings" 11 "time" 12 13 "github.com/google/syzkaller/pkg/log" 14 "github.com/google/syzkaller/pkg/osutil" 15 "github.com/google/syzkaller/sys/targets" 16 ) 17 18 type gvisor struct{} 19 20 var bazelTargetPath = regexp.MustCompile(`(?sm:.*^)\s*Outputs: \[(.*)\](?sm:$.*)`) 21 22 func (gvisor gvisor) build(params Params) (ImageDetails, error) { 23 if params.Compiler == "" { 24 params.Compiler = "bazel" 25 } 26 27 // Bring down bazel daemon right away. We don't need it running and consuming memory. 28 defer osutil.RunCmd(10*time.Minute, params.KernelDir, params.Compiler, "shutdown") 29 30 config, err := parseGVisorConfig(params.Config) 31 if err != nil { 32 return ImageDetails{}, fmt.Errorf("cannot parse gVisor configuration: %w", err) 33 } 34 args := []string{} 35 36 target := "//runsc:runsc" 37 if config.Race { 38 args = append(args, "--config=race") 39 target = "//runsc:runsc-race" 40 } 41 if config.Coverage { 42 coverageFiles := "//pkg/..." 43 exclusions := []string{ 44 "//pkg/sentry/platform", "//pkg/ring0", // Breaks kvm. 45 "//pkg/coverage:coverage", // Too slow. 46 } 47 if config.Race { 48 // These targets use go:norace, which is not 49 // respected by coverage instrumentation. Race builds 50 // will be instrumented with atomic coverage (using 51 // sync/atomic.AddInt32), which will not work. 52 exclusions = append(exclusions, []string{ 53 "//pkg/sleep:sleep", 54 "//pkg/sync:sync", 55 "//pkg/syncevent:syncevent", 56 }...) 57 } 58 for _, f := range exclusions { 59 coverageFiles += ",-" + f 60 } 61 args = append(args, []string{ 62 "--collect_code_coverage", 63 "--instrumentation_filter=" + coverageFiles}...) 64 } 65 buildArgs := []string{"build", "--verbose_failures"} 66 buildArgs = append(buildArgs, args...) 67 buildArgs = append(buildArgs, target) 68 log.Logf(0, "bazel: %v", buildArgs) 69 // The 1 hour timeout is quite high. But we've seen false positives with 20 mins 70 // on the first build after bazel/deps update. Also other gvisor instances running 71 // on the same machine contribute to longer build times. 72 if _, err := osutil.RunCmd(60*time.Minute, params.KernelDir, params.Compiler, buildArgs...); err != nil { 73 return ImageDetails{}, err 74 } 75 76 // Find out a path to the runsc binary. 77 aqueryArgs := append([]string{"aquery"}, args...) 78 aqueryArgs = append(aqueryArgs, fmt.Sprintf("mnemonic(\"GoLink\", %s)", target)) 79 log.Logf(0, "bazel: %v", aqueryArgs) 80 out, err := osutil.RunCmd(10*time.Minute, params.KernelDir, params.Compiler, aqueryArgs...) 81 if err != nil { 82 return ImageDetails{}, err 83 } 84 85 match := bazelTargetPath.FindSubmatch(out) 86 if match == nil { 87 return ImageDetails{}, fmt.Errorf("failed to find the runsc binary") 88 } 89 outBinary := filepath.Join(params.KernelDir, filepath.FromSlash(string(match[1]))) 90 91 if err := osutil.CopyFile(outBinary, filepath.Join(params.OutputDir, "image")); err != nil { 92 return ImageDetails{}, err 93 } 94 sysTarget := targets.Get(params.TargetOS, params.TargetArch) 95 return ImageDetails{}, osutil.CopyFile(outBinary, filepath.Join(params.OutputDir, "obj", sysTarget.KernelObject)) 96 } 97 98 func (gvisor) clean(kernelDir, targetArch string) error { 99 // Let's assume that bazel always properly handles build without cleaning (until proven otherwise). 100 return nil 101 } 102 103 // Known gVisor configuration flags. 104 const ( 105 gvisorFlagCover = "-cover" 106 gvisorFlagRace = "-race" 107 ) 108 109 // gvisorConfig is a gVisor configuration. 110 type gvisorConfig struct { 111 // Coverage represents whether code coverage is enabled. 112 Coverage bool 113 114 // Race represents whether race condition detection is enabled. 115 Race bool 116 } 117 118 // parseGVisorConfig parses a set of flags into a `gvisorConfig`. 119 func parseGVisorConfig(config []byte) (gvisorConfig, error) { 120 var cfg gvisorConfig 121 for _, flag := range strings.Fields(string(config)) { 122 switch flag { 123 case gvisorFlagCover: 124 cfg.Coverage = true 125 case gvisorFlagRace: 126 cfg.Race = true 127 default: 128 return cfg, fmt.Errorf("unknown gVisor configuration flag: %q", flag) 129 } 130 } 131 return cfg, nil 132 }