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