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 }