github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/sys/syz-extract/extract.go (about) 1 // Copyright 2016 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 main 5 6 import ( 7 "bytes" 8 "flag" 9 "fmt" 10 "os" 11 "path/filepath" 12 "runtime" 13 "sort" 14 "strings" 15 16 "github.com/google/syzkaller/pkg/ast" 17 "github.com/google/syzkaller/pkg/compiler" 18 "github.com/google/syzkaller/pkg/osutil" 19 "github.com/google/syzkaller/pkg/tool" 20 "github.com/google/syzkaller/sys/targets" 21 ) 22 23 var ( 24 flagOS = flag.String("os", runtime.GOOS, "target OS") 25 flagBuild = flag.Bool("build", false, "regenerate arch-specific kernel headers") 26 flagSourceDir = flag.String("sourcedir", "", "path to kernel source checkout dir") 27 flagIncludes = flag.String("includedirs", "", "path to other kernel source include dirs separated by commas") 28 flagBuildDir = flag.String("builddir", "", "path to kernel build dir") 29 flagArch = flag.String("arch", "", "comma-separated list of arches to generate (all by default)") 30 ) 31 32 type Arch struct { 33 target *targets.Target 34 sourceDir string 35 includeDirs string 36 buildDir string 37 build bool 38 files []*File 39 err error 40 done chan bool 41 } 42 43 type File struct { 44 arch *Arch 45 name string 46 consts map[string]uint64 47 undeclared map[string]bool 48 info *compiler.ConstInfo 49 err error 50 done chan bool 51 } 52 53 type Extractor interface { 54 prepare(sourcedir string, build bool, arches []*Arch) error 55 prepareArch(arch *Arch) error 56 processFile(arch *Arch, info *compiler.ConstInfo) (map[string]uint64, map[string]bool, error) 57 } 58 59 var extractors = map[string]Extractor{ 60 targets.Linux: new(linux), 61 targets.FreeBSD: new(freebsd), 62 targets.Darwin: new(darwin), 63 targets.NetBSD: new(netbsd), 64 targets.OpenBSD: new(openbsd), 65 "android": new(linux), 66 targets.Fuchsia: new(fuchsia), 67 targets.Windows: new(windows), 68 targets.Trusty: new(trusty), 69 } 70 71 func main() { 72 flag.Parse() 73 if *flagBuild && *flagBuildDir != "" { 74 tool.Failf("-build and -builddir is an invalid combination") 75 } 76 OS := *flagOS 77 extractor := extractors[OS] 78 if extractor == nil { 79 tool.Failf("unknown os: %v", OS) 80 } 81 arches, nfiles, err := createArches(OS, archList(OS, *flagArch), flag.Args()) 82 if err != nil { 83 tool.Fail(err) 84 } 85 if *flagSourceDir == "" { 86 tool.Fail(fmt.Errorf("provide path to kernel checkout via -sourcedir " + 87 "flag (or make extract SOURCEDIR)")) 88 } 89 if err := extractor.prepare(*flagSourceDir, *flagBuild, arches); err != nil { 90 tool.Fail(err) 91 } 92 93 jobC := make(chan interface{}, len(arches)+nfiles) 94 for _, arch := range arches { 95 jobC <- arch 96 } 97 98 for p := 0; p < runtime.GOMAXPROCS(0); p++ { 99 go worker(extractor, jobC) 100 } 101 102 failed := false 103 constFiles := make(map[string]*compiler.ConstFile) 104 for _, arch := range arches { 105 fmt.Printf("generating %v/%v...\n", OS, arch.target.Arch) 106 <-arch.done 107 if arch.err != nil { 108 failed = true 109 fmt.Printf("%v\n", arch.err) 110 continue 111 } 112 for _, f := range arch.files { 113 <-f.done 114 if f.err != nil { 115 failed = true 116 fmt.Printf("%v: %v\n", f.name, f.err) 117 continue 118 } 119 if constFiles[f.name] == nil { 120 constFiles[f.name] = compiler.NewConstFile() 121 } 122 constFiles[f.name].AddArch(f.arch.target.Arch, f.consts, f.undeclared) 123 } 124 } 125 for file, cf := range constFiles { 126 outname := filepath.Join("sys", OS, file+".const") 127 data := cf.Serialize() 128 if len(data) == 0 { 129 os.Remove(outname) 130 continue 131 } 132 if err := osutil.WriteFile(outname, data); err != nil { 133 tool.Failf("failed to write output file: %v", err) 134 } 135 } 136 137 if !failed && *flagArch == "" { 138 failed = checkUnsupportedCalls(arches) 139 } 140 for _, arch := range arches { 141 if arch.build { 142 os.RemoveAll(arch.buildDir) 143 } 144 } 145 if failed { 146 os.Exit(1) 147 } 148 } 149 150 func worker(extractor Extractor, jobC chan interface{}) { 151 for job := range jobC { 152 switch j := job.(type) { 153 case *Arch: 154 infos, err := processArch(extractor, j) 155 j.err = err 156 close(j.done) 157 if j.err == nil { 158 for _, f := range j.files { 159 f.info = infos[filepath.Join("sys", j.target.OS, f.name)] 160 jobC <- f 161 } 162 } 163 case *File: 164 j.consts, j.undeclared, j.err = processFile(extractor, j.arch, j) 165 close(j.done) 166 } 167 } 168 } 169 170 func createArches(OS string, archArray, files []string) ([]*Arch, int, error) { 171 errBuf := new(bytes.Buffer) 172 eh := func(pos ast.Pos, msg string) { 173 fmt.Fprintf(errBuf, "%v: %v\n", pos, msg) 174 } 175 top := ast.ParseGlob(filepath.Join("sys", OS, "*.txt"), eh) 176 if top == nil { 177 return nil, 0, fmt.Errorf("%v", errBuf.String()) 178 } 179 allFiles := compiler.FileList(top, OS, eh) 180 if allFiles == nil { 181 return nil, 0, fmt.Errorf("%v", errBuf.String()) 182 } 183 if len(files) == 0 { 184 for file := range allFiles { 185 files = append(files, file) 186 } 187 } 188 nfiles := 0 189 var arches []*Arch 190 for _, archStr := range archArray { 191 buildDir := "" 192 if *flagBuild { 193 dir, err := os.MkdirTemp("", "syzkaller-kernel-build") 194 if err != nil { 195 return nil, 0, fmt.Errorf("failed to create temp dir: %w", err) 196 } 197 buildDir = dir 198 } else if *flagBuildDir != "" { 199 buildDir = *flagBuildDir 200 } else { 201 buildDir = *flagSourceDir 202 } 203 204 target := targets.Get(OS, archStr) 205 if target == nil { 206 return nil, 0, fmt.Errorf("unknown arch: %v", archStr) 207 } 208 209 arch := &Arch{ 210 target: target, 211 sourceDir: *flagSourceDir, 212 includeDirs: *flagIncludes, 213 buildDir: buildDir, 214 build: *flagBuild, 215 done: make(chan bool), 216 } 217 var archFiles []string 218 for _, file := range files { 219 meta, ok := allFiles[file] 220 if !ok { 221 return nil, 0, fmt.Errorf("unknown file: %v", file) 222 } 223 if meta.NoExtract || !meta.SupportsArch(archStr) { 224 continue 225 } 226 archFiles = append(archFiles, file) 227 } 228 sort.Strings(archFiles) 229 for _, f := range archFiles { 230 arch.files = append(arch.files, &File{ 231 arch: arch, 232 name: f, 233 done: make(chan bool), 234 }) 235 } 236 arches = append(arches, arch) 237 nfiles += len(arch.files) 238 } 239 return arches, nfiles, nil 240 } 241 242 func archList(OS, arches string) []string { 243 if arches != "" { 244 return strings.Split(arches, ",") 245 } 246 var archArray []string 247 for arch := range targets.List[OS] { 248 archArray = append(archArray, arch) 249 } 250 sort.Strings(archArray) 251 return archArray 252 } 253 254 func checkUnsupportedCalls(arches []*Arch) bool { 255 supported := make(map[string]bool) 256 unsupported := make(map[string]string) 257 for _, arch := range arches { 258 for _, f := range arch.files { 259 for name := range f.consts { 260 supported[name] = true 261 } 262 for name := range f.undeclared { 263 unsupported[name] = f.name 264 } 265 } 266 } 267 failed := false 268 for name, file := range unsupported { 269 if supported[name] { 270 continue 271 } 272 failed = true 273 fmt.Printf("%v: %v is unsupported on all arches (typo?)\n", 274 file, name) 275 } 276 return failed 277 } 278 279 func processArch(extractor Extractor, arch *Arch) (map[string]*compiler.ConstInfo, error) { 280 errBuf := new(bytes.Buffer) 281 eh := func(pos ast.Pos, msg string) { 282 fmt.Fprintf(errBuf, "%v: %v\n", pos, msg) 283 } 284 top := ast.ParseGlob(filepath.Join("sys", arch.target.OS, "*.txt"), eh) 285 if top == nil { 286 return nil, fmt.Errorf("%v", errBuf.String()) 287 } 288 infos := compiler.ExtractConsts(top, arch.target, eh) 289 if infos == nil { 290 return nil, fmt.Errorf("%v", errBuf.String()) 291 } 292 if err := extractor.prepareArch(arch); err != nil { 293 return nil, err 294 } 295 return infos, nil 296 } 297 298 func processFile(extractor Extractor, arch *Arch, file *File) (map[string]uint64, map[string]bool, error) { 299 inname := filepath.Join("sys", arch.target.OS, file.name) 300 if file.info == nil { 301 return nil, nil, fmt.Errorf("const info for input file %v is missing", inname) 302 } 303 if len(file.info.Consts) == 0 { 304 return nil, nil, nil 305 } 306 return extractor.processFile(arch, file.info) 307 }