github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/tools/syz-bisect/bisect.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 // syz-bisect runs bisection to find cause/fix commit for a crash. 5 // 6 // The tool is originally created to test pkg/bisect logic. 7 // 8 // The tool requires a config file passed in -config flag, see Config type below for details, 9 // and a directory with info about the crash passed in -crash flag). 10 // If -fix flag is specified, it does fix bisection. Otherwise it does cause bisection. Also 11 // wanted syzkaller and kernel commits can be specified using -syzkaller_commit and 12 // -kernel_commit. HEAD is used if commits are not specified. 13 // 14 // The crash dir should contain the following files: 15 // - repro.cprog or repro.prog: reproducer for the crash 16 // - repro.opts: syzkaller reproducer options (e.g. {"procs":1,"sandbox":"none",...}) (optional) 17 // 18 // The tool stores bisection result into cause.commit or fix.commit. 19 package main 20 21 import ( 22 "encoding/json" 23 "flag" 24 "fmt" 25 "os" 26 "path/filepath" 27 28 "github.com/google/syzkaller/pkg/bisect" 29 "github.com/google/syzkaller/pkg/config" 30 "github.com/google/syzkaller/pkg/debugtracer" 31 "github.com/google/syzkaller/pkg/mgrconfig" 32 "github.com/google/syzkaller/pkg/osutil" 33 "github.com/google/syzkaller/pkg/vcs" 34 ) 35 36 var ( 37 flagConfig = flag.String("config", "", "bisect config file") 38 flagCrash = flag.String("crash", "", "dir with crash info") 39 flagFix = flag.Bool("fix", false, "search for crash fix") 40 flagKernelCommit = flag.String("kernel_commit", "", "original kernel commit") 41 flagKernelCommitTitle = flag.String("kernel_commit_title", "", "original kernel commit title") 42 flagSyzkallerCommit = flag.String("syzkaller_commit", "", "original syzkaller commit") 43 ) 44 45 type Config struct { 46 Compiler string `json:"compiler"` 47 // Currently either 'gcc' or 'clang'. Note that pkg/bisect requires 48 // explicit plumbing for every os/compiler combination. 49 CompilerType string `json:"compiler_type"` 50 // BinDir must point to a dir that contains compilers required to build 51 // older versions of the kernel. For linux, it needs to include several 52 // compiler versions. 53 BinDir string `json:"bin_dir"` 54 Linker string `json:"linker"` 55 Ccache string `json:"ccache"` 56 KernelRepo string `json:"kernel_repo"` 57 KernelBranch string `json:"kernel_branch"` 58 SyzkallerRepo string `json:"syzkaller_repo"` 59 // Directory with user-space system for building kernel images 60 // (for linux that's the input to tools/create-gce-image.sh). 61 Userspace string `json:"userspace"` 62 // Sysctl/cmdline files used to build the image which was used to crash the kernel, e.g. see: 63 // dashboard/config/upstream.sysctl 64 // dashboard/config/upstream-selinux.cmdline 65 Sysctl string `json:"sysctl"` 66 Cmdline string `json:"cmdline"` 67 CrossTree bool `json:"cross_tree"` 68 Backports []vcs.BackportCommit `json:"backports"` 69 70 KernelConfig string `json:"kernel_config"` 71 KernelBaselineConfig string `json:"kernel_baseline_config"` 72 73 // Manager config that was used to obtain the crash. 74 Manager json.RawMessage `json:"manager"` 75 } 76 77 func main() { 78 flag.Parse() 79 os.Setenv("SYZ_DISABLE_SANDBOXING", "yes") 80 mycfg := new(Config) 81 if err := config.LoadFile(*flagConfig, mycfg); err != nil { 82 fmt.Fprintln(os.Stderr, err) 83 os.Exit(1) 84 } 85 mgrcfg, err := mgrconfig.LoadData(mycfg.Manager) 86 if err != nil { 87 fmt.Fprintln(os.Stderr, err) 88 os.Exit(1) 89 } 90 if mgrcfg.Workdir == "" { 91 mgrcfg.Workdir, err = os.MkdirTemp("", "syz-bisect") 92 if err != nil { 93 fmt.Fprintf(os.Stderr, "failed to create temp dir: %v\n", err) 94 os.Exit(1) 95 } 96 defer os.RemoveAll(mgrcfg.Workdir) 97 } 98 cfg := &bisect.Config{ 99 Trace: &debugtracer.GenericTracer{ 100 TraceWriter: os.Stdout, 101 OutDir: *flagCrash, 102 }, 103 Fix: *flagFix, 104 DefaultCompiler: mycfg.Compiler, 105 CompilerType: mycfg.CompilerType, 106 Linker: mycfg.Linker, 107 BinDir: mycfg.BinDir, 108 Ccache: mycfg.Ccache, 109 CrossTree: mycfg.CrossTree, 110 Kernel: bisect.KernelConfig{ 111 Repo: mycfg.KernelRepo, 112 Branch: mycfg.KernelBranch, 113 Commit: *flagKernelCommit, 114 CommitTitle: *flagKernelCommitTitle, 115 Userspace: mycfg.Userspace, 116 Sysctl: mycfg.Sysctl, 117 Cmdline: mycfg.Cmdline, 118 Backports: mycfg.Backports, 119 }, 120 Syzkaller: bisect.SyzkallerConfig{ 121 Repo: mycfg.SyzkallerRepo, 122 Commit: *flagSyzkallerCommit, 123 }, 124 Manager: mgrcfg, 125 } 126 loadFile("", mycfg.KernelConfig, &cfg.Kernel.Config, true) 127 loadFile("", mycfg.KernelBaselineConfig, &cfg.Kernel.BaselineConfig, false) 128 loadFile(*flagCrash, "repro.prog", &cfg.Repro.Syz, false) 129 loadFile(*flagCrash, "repro.cprog", &cfg.Repro.C, false) 130 loadFile(*flagCrash, "repro.opts", &cfg.Repro.Opts, false) 131 132 if len(cfg.Repro.Syz) == 0 && len(cfg.Repro.C) == 0 { 133 fmt.Fprintf(os.Stderr, "no repro.cprog or repro.prog found\n") 134 os.Exit(1) 135 } 136 137 if cfg.Syzkaller.Commit == "" { 138 cfg.Syzkaller.Commit = vcs.HEAD 139 } 140 if cfg.Kernel.Commit == "" { 141 cfg.Kernel.Commit = vcs.HEAD 142 } 143 144 result, err := bisect.Run(cfg) 145 if err != nil { 146 fmt.Fprintf(os.Stderr, "bisection failed: %v\n", err) 147 os.Exit(1) 148 } 149 150 saveResultCommits(result.Commits) 151 } 152 153 func loadFile(path, file string, dst *[]byte, mandatory bool) { 154 filename := filepath.Join(path, file) 155 if !mandatory && !osutil.IsExist(filename) { 156 return 157 } 158 data, err := os.ReadFile(filename) 159 if err != nil { 160 fmt.Fprintln(os.Stderr, err) 161 os.Exit(1) 162 } 163 *dst = data 164 } 165 166 func saveResultCommits(commits []*vcs.Commit) { 167 var result string 168 if len(commits) > 0 { 169 for _, commit := range commits { 170 result = result + commit.Hash + "\n" 171 } 172 } else if *flagFix { 173 result = "the crash still happens on HEAD\n" 174 } else { 175 result = "the crash already happened on the oldest tested release\n" 176 } 177 178 var fileName string 179 if *flagFix { 180 fileName = filepath.Join(*flagCrash, "fix.commit") 181 } else { 182 fileName = filepath.Join(*flagCrash, "cause.commit") 183 } 184 osutil.WriteFile(fileName, []byte(result)) 185 }