github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/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 Make string `json:"make"` 51 // BinDir must point to a dir that contains compilers required to build 52 // older versions of the kernel. For linux, it needs to include several 53 // compiler versions. 54 BinDir string `json:"bin_dir"` 55 Linker string `json:"linker"` 56 Ccache string `json:"ccache"` 57 KernelRepo string `json:"kernel_repo"` 58 KernelBranch string `json:"kernel_branch"` 59 SyzkallerRepo string `json:"syzkaller_repo"` 60 // Directory with user-space system for building kernel images 61 // (for linux that's the input to tools/create-gce-image.sh). 62 Userspace string `json:"userspace"` 63 // Sysctl/cmdline files used to build the image which was used to crash the kernel, e.g. see: 64 // dashboard/config/upstream.sysctl 65 // dashboard/config/upstream-selinux.cmdline 66 Sysctl string `json:"sysctl"` 67 Cmdline string `json:"cmdline"` 68 CrossTree bool `json:"cross_tree"` 69 Backports []vcs.BackportCommit `json:"backports"` 70 71 KernelConfig string `json:"kernel_config"` 72 KernelBaselineConfig string `json:"kernel_baseline_config"` 73 74 // Manager config that was used to obtain the crash. 75 Manager json.RawMessage `json:"manager"` 76 } 77 78 func main() { 79 flag.Parse() 80 os.Setenv("SYZ_DISABLE_SANDBOXING", "yes") 81 mycfg := new(Config) 82 if err := config.LoadFile(*flagConfig, mycfg); err != nil { 83 fmt.Fprintln(os.Stderr, err) 84 os.Exit(1) 85 } 86 mgrcfg, err := mgrconfig.LoadData(mycfg.Manager) 87 if err != nil { 88 fmt.Fprintln(os.Stderr, err) 89 os.Exit(1) 90 } 91 if mgrcfg.Workdir == "" { 92 mgrcfg.Workdir, err = os.MkdirTemp("", "syz-bisect") 93 if err != nil { 94 fmt.Fprintf(os.Stderr, "failed to create temp dir: %v\n", err) 95 os.Exit(1) 96 } 97 defer os.RemoveAll(mgrcfg.Workdir) 98 } 99 cfg := &bisect.Config{ 100 Trace: &debugtracer.GenericTracer{ 101 TraceWriter: os.Stdout, 102 OutDir: *flagCrash, 103 }, 104 Fix: *flagFix, 105 DefaultCompiler: mycfg.Compiler, 106 CompilerType: mycfg.CompilerType, 107 Make: mycfg.Make, 108 Linker: mycfg.Linker, 109 BinDir: mycfg.BinDir, 110 Ccache: mycfg.Ccache, 111 CrossTree: mycfg.CrossTree, 112 Kernel: bisect.KernelConfig{ 113 Repo: mycfg.KernelRepo, 114 Branch: mycfg.KernelBranch, 115 Commit: *flagKernelCommit, 116 CommitTitle: *flagKernelCommitTitle, 117 Userspace: mycfg.Userspace, 118 Sysctl: mycfg.Sysctl, 119 Cmdline: mycfg.Cmdline, 120 Backports: mycfg.Backports, 121 }, 122 Syzkaller: bisect.SyzkallerConfig{ 123 Repo: mycfg.SyzkallerRepo, 124 Commit: *flagSyzkallerCommit, 125 }, 126 Manager: mgrcfg, 127 } 128 loadFile("", mycfg.KernelConfig, &cfg.Kernel.Config, true) 129 loadFile("", mycfg.KernelBaselineConfig, &cfg.Kernel.BaselineConfig, false) 130 loadFile(*flagCrash, "repro.prog", &cfg.Repro.Syz, false) 131 loadFile(*flagCrash, "repro.cprog", &cfg.Repro.C, false) 132 loadFile(*flagCrash, "repro.opts", &cfg.Repro.Opts, false) 133 134 if len(cfg.Repro.Syz) == 0 && len(cfg.Repro.C) == 0 { 135 fmt.Fprintf(os.Stderr, "no repro.cprog or repro.prog found\n") 136 os.Exit(1) 137 } 138 139 if cfg.Syzkaller.Commit == "" { 140 cfg.Syzkaller.Commit = vcs.HEAD 141 } 142 if cfg.Kernel.Commit == "" { 143 cfg.Kernel.Commit = vcs.HEAD 144 } 145 146 result, err := bisect.Run(cfg) 147 if err != nil { 148 fmt.Fprintf(os.Stderr, "bisection failed: %v\n", err) 149 os.Exit(1) 150 } 151 152 saveResultCommits(result.Commits) 153 } 154 155 func loadFile(path, file string, dst *[]byte, mandatory bool) { 156 filename := filepath.Join(path, file) 157 if !mandatory && !osutil.IsExist(filename) { 158 return 159 } 160 data, err := os.ReadFile(filename) 161 if err != nil { 162 fmt.Fprintln(os.Stderr, err) 163 os.Exit(1) 164 } 165 *dst = data 166 } 167 168 func saveResultCommits(commits []*vcs.Commit) { 169 var result string 170 if len(commits) > 0 { 171 for _, commit := range commits { 172 result = result + commit.Hash + "\n" 173 } 174 } else if *flagFix { 175 result = "the crash still happens on HEAD\n" 176 } else { 177 result = "the crash already happened on the oldest tested release\n" 178 } 179 180 var fileName string 181 if *flagFix { 182 fileName = filepath.Join(*flagCrash, "fix.commit") 183 } else { 184 fileName = filepath.Join(*flagCrash, "cause.commit") 185 } 186 osutil.WriteFile(fileName, []byte(result)) 187 }