github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/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/instance" 34 "github.com/google/syzkaller/pkg/mgrconfig" 35 "github.com/google/syzkaller/pkg/osutil" 36 "github.com/google/syzkaller/pkg/tool" 37 "github.com/google/syzkaller/pkg/vcs" 38 ) 39 40 var ( 41 flagOS = flag.String("os", runtime.GOOS, "OS to test") 42 flagArch = flag.String("arch", runtime.GOARCH, "arch to test") 43 flagKernelSrc = flag.String("kernel_src", "", "path to kernel checkout") 44 flagKernelConfig = flag.String("config", "", "kernel config") 45 flagKernelSysctl = flag.String("sysctl", "", "kernel sysctl file") 46 flagKernelCmdline = flag.String("cmdline", "", "kernel cmdline file") 47 flagUserspace = flag.String("userspace", "", "path to userspace for build") 48 flagBisectBin = flag.String("bisect_bin", "", "path to bisection binaries") 49 flagSyzkaller = flag.String("syzkaller", ".", "path to built syzkaller") 50 flagSandbox = flag.String("sandbox", "namespace", "sandbox to use for testing") 51 flagSandboxArg = flag.Int("sandbox_arg", 0, "an argument for sandbox runner") 52 flagCompiler = flag.String("compiler", "clang", "compiler to use") 53 flagCompilerType = flag.String("compiler_type", "clang", "compiler to use") 54 flagLinker = flag.String("linker", "ld.lld", "linker to use") 55 ) 56 57 const ( 58 vmType = "qemu" 59 numTests = 5 60 ) 61 62 func main() { 63 flag.Parse() 64 if os.Getuid() != 0 { 65 tool.Failf("image build will fail, run under root") 66 } 67 os.Setenv("SYZ_DISABLE_SANDBOXING", "yes") 68 dir, err := os.MkdirTemp("", "syz-testbuild") 69 if err != nil { 70 tool.Fail(err) 71 } 72 defer os.RemoveAll(dir) 73 cfg := &mgrconfig.Config{ 74 RawTarget: *flagOS + "/" + *flagArch, 75 HTTP: ":0", 76 Workdir: dir, 77 KernelSrc: *flagKernelSrc, 78 KernelObj: *flagKernelSrc, 79 Syzkaller: *flagSyzkaller, 80 Sandbox: *flagSandbox, 81 SandboxArg: int64(*flagSandboxArg), 82 SSHUser: "root", 83 Procs: 1, 84 Cover: false, 85 Type: vmType, 86 VM: json.RawMessage([]byte(fmt.Sprintf(`{"count": %v, 87 "cpu": 2, 88 "mem": 2048, 89 "cmdline": "root=/dev/sda1"}`, numTests))), 90 Derived: mgrconfig.Derived{ 91 TargetOS: *flagOS, 92 TargetArch: *flagArch, 93 TargetVMArch: *flagArch, 94 }, 95 Experimental: mgrconfig.Experimental{ 96 DescriptionsMode: "manual", 97 }, 98 } 99 if err := mgrconfig.SetTargets(cfg); err != nil { 100 tool.Fail(err) 101 } 102 if err := mgrconfig.Complete(cfg); err != nil { 103 tool.Fail(err) 104 } 105 repo, err := vcs.NewRepo(*flagOS, vmType, *flagKernelSrc) 106 if err != nil { 107 tool.Fail(err) 108 } 109 bisecter := repo.(vcs.Bisecter) 110 head, err := repo.Commit(vcs.HEAD) 111 if err != nil { 112 tool.Fail(err) 113 } 114 log.Printf("HEAD is on %v %v", head.Hash, head.Title) 115 tags, err := bisecter.PreviousReleaseTags(head.Hash, "gcc") 116 if err != nil { 117 tool.Fail(err) 118 } 119 log.Printf("tags: %v", tags) 120 kernelConfig, err := os.ReadFile(*flagKernelConfig) 121 if err != nil { 122 tool.Fail(err) 123 } 124 env, err := instance.NewEnv(cfg, nil, nil) 125 if err != nil { 126 tool.Fail(err) 127 } 128 test(repo, bisecter, kernelConfig, env, head) 129 for _, tag := range tags { 130 com, err := repo.SwitchCommit(tag) 131 if err != nil { 132 tool.Fail(err) 133 } 134 test(repo, bisecter, kernelConfig, env, com) 135 } 136 } 137 138 func test(repo vcs.Repo, bisecter vcs.Bisecter, kernelConfig []byte, env instance.Env, com *vcs.Commit) { 139 bisectEnv, err := bisecter.EnvForCommit(*flagCompiler, *flagCompilerType, *flagBisectBin, com.Hash, kernelConfig, nil) 140 if err != nil { 141 tool.Fail(err) 142 } 143 log.Printf("testing: %v %v using %v", com.Hash, com.Title, bisectEnv.Compiler) 144 buildCfg := &instance.BuildKernelConfig{ 145 CompilerBin: bisectEnv.Compiler, 146 LinkerBin: *flagLinker, 147 CcacheBin: "", 148 UserspaceDir: *flagUserspace, 149 CmdlineFile: *flagKernelCmdline, 150 SysctlFile: *flagKernelSysctl, 151 KernelConfig: bisectEnv.KernelConfig, 152 } 153 if err := env.CleanKernel(buildCfg); err != nil { 154 tool.Fail(err) 155 } 156 _, _, err = env.BuildKernel(buildCfg) 157 if err != nil { 158 var verr *osutil.VerboseError 159 if errors.As(err, &verr) { 160 log.Printf("BUILD BROKEN: %s", verr) 161 saveLog(com.Hash, 0, verr.Output) 162 } else { 163 log.Printf("BUILD BROKEN: %v", err) 164 } 165 return 166 } 167 log.Printf("build OK") 168 results, err := env.Test(numTests, nil, nil, nil) 169 if err != nil { 170 tool.Fail(err) 171 } 172 var verdicts []string 173 for i, res := range results { 174 if res.Error == nil { 175 verdicts = append(verdicts, "OK") 176 continue 177 } 178 179 var testError *instance.TestError 180 var crashError *instance.CrashError 181 switch { 182 case errors.As(res.Error, &testError): 183 if testError.Boot { 184 verdicts = append(verdicts, fmt.Sprintf("boot failed: %v", testError)) 185 } else { 186 verdicts = append(verdicts, fmt.Sprintf("basic kernel testing failed: %v", testError)) 187 } 188 output := testError.Output 189 if testError.Report != nil { 190 output = testError.Report.Output 191 } 192 saveLog(com.Hash, i, output) 193 case errors.As(res.Error, &crashError): 194 verdicts = append(verdicts, fmt.Sprintf("crashed: %v", crashError)) 195 output := crashError.Report.Report 196 if len(output) == 0 { 197 output = crashError.Report.Output 198 } 199 saveLog(com.Hash, i, output) 200 default: 201 verdicts = append(verdicts, fmt.Sprintf("failed: %v", err)) 202 } 203 } 204 unique := make(map[string]bool) 205 for _, verdict := range verdicts { 206 unique[verdict] = true 207 } 208 if len(unique) == 1 { 209 log.Printf("all runs: %v", verdicts[0]) 210 } else { 211 for i, verdict := range verdicts { 212 log.Printf("run #%v: %v", i, verdict) 213 } 214 } 215 } 216 217 func saveLog(hash string, idx int, data []byte) { 218 if len(data) == 0 { 219 return 220 } 221 osutil.WriteFile(fmt.Sprintf("%v.%v", hash, idx), data) 222 }