github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/sys/syz-sysgen/sysgen.go (about) 1 // Copyright 2015/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 "io" 11 "os" 12 "path/filepath" 13 "reflect" 14 "sort" 15 "strings" 16 "sync" 17 "text/template" 18 19 "github.com/google/syzkaller/pkg/ast" 20 "github.com/google/syzkaller/pkg/compiler" 21 "github.com/google/syzkaller/pkg/hash" 22 "github.com/google/syzkaller/pkg/osutil" 23 "github.com/google/syzkaller/pkg/serializer" 24 "github.com/google/syzkaller/pkg/tool" 25 "github.com/google/syzkaller/prog" 26 "github.com/google/syzkaller/sys/targets" 27 ) 28 29 type SyscallData struct { 30 Name string 31 CallName string 32 NR int32 33 NeedCall bool 34 Attrs []uint64 35 } 36 37 type Define struct { 38 Name string 39 Value string 40 } 41 42 type ArchData struct { 43 Revision string 44 ForkServer int 45 Shmem int 46 GOARCH string 47 PageSize uint64 48 NumPages uint64 49 DataOffset uint64 50 Calls []SyscallData 51 Defines []Define 52 } 53 54 type OSData struct { 55 GOOS string 56 Archs []ArchData 57 } 58 59 type CallPropDescription struct { 60 Type string 61 Name string 62 } 63 64 type ExecutorData struct { 65 OSes []OSData 66 CallAttrs []string 67 CallProps []CallPropDescription 68 } 69 70 var srcDir = flag.String("src", "", "path to root of syzkaller source dir") 71 var outDir = flag.String("out", "", "path to out dir") 72 73 func main() { 74 defer tool.Init()() 75 76 var OSList []string 77 for OS := range targets.List { 78 OSList = append(OSList, OS) 79 } 80 sort.Strings(OSList) 81 82 data := &ExecutorData{} 83 for _, OS := range OSList { 84 descriptions := ast.ParseGlob(filepath.Join(*srcDir, "sys", OS, "*.txt"), nil) 85 if descriptions == nil { 86 os.Exit(1) 87 } 88 constFile := compiler.DeserializeConstFile(filepath.Join(*srcDir, "sys", OS, "*.const"), nil) 89 if constFile == nil { 90 os.Exit(1) 91 } 92 osutil.MkdirAll(filepath.Join(*outDir, "sys", OS, "gen")) 93 94 var archs []string 95 for arch := range targets.List[OS] { 96 archs = append(archs, arch) 97 } 98 sort.Strings(archs) 99 100 var jobs []*Job 101 for _, arch := range archs { 102 target := targets.List[OS][arch] 103 constInfo := compiler.ExtractConsts(descriptions, target, nil) 104 if OS == targets.TestOS { 105 // The ConstFile object provides no guarantees re concurrent read-write, 106 // so let's patch it before we start goroutines. 107 compiler.FabricateSyscallConsts(target, constInfo, constFile) 108 } 109 jobs = append(jobs, &Job{ 110 Target: target, 111 Unsupported: make(map[string]bool), 112 ConstInfo: constInfo, 113 }) 114 } 115 sort.Slice(jobs, func(i, j int) bool { 116 return jobs[i].Target.Arch < jobs[j].Target.Arch 117 }) 118 var wg sync.WaitGroup 119 wg.Add(len(jobs)) 120 121 for _, job := range jobs { 122 job := job 123 go func() { 124 defer wg.Done() 125 processJob(job, descriptions, constFile) 126 }() 127 } 128 wg.Wait() 129 130 var syscallArchs []ArchData 131 unsupported := make(map[string]int) 132 for _, job := range jobs { 133 if !job.OK { 134 fmt.Printf("compilation of %v/%v target failed:\n", job.Target.OS, job.Target.Arch) 135 for _, msg := range job.Errors { 136 fmt.Print(msg) 137 } 138 os.Exit(1) 139 } 140 syscallArchs = append(syscallArchs, job.ArchData) 141 for u := range job.Unsupported { 142 unsupported[u]++ 143 } 144 } 145 data.OSes = append(data.OSes, OSData{ 146 GOOS: OS, 147 Archs: syscallArchs, 148 }) 149 150 for what, count := range unsupported { 151 if count == len(jobs) { 152 tool.Failf("%v is unsupported on all arches (typo?)", what) 153 } 154 } 155 } 156 157 attrs := reflect.TypeOf(prog.SyscallAttrs{}) 158 for i := 0; i < attrs.NumField(); i++ { 159 data.CallAttrs = append(data.CallAttrs, prog.CppName(attrs.Field(i).Name)) 160 } 161 162 props := prog.CallProps{} 163 props.ForeachProp(func(name, _ string, value reflect.Value) { 164 data.CallProps = append(data.CallProps, CallPropDescription{ 165 Type: value.Kind().String(), 166 Name: prog.CppName(name), 167 }) 168 }) 169 170 writeExecutorSyscalls(data) 171 } 172 173 type Job struct { 174 Target *targets.Target 175 OK bool 176 Errors []string 177 Unsupported map[string]bool 178 ArchData ArchData 179 ConstInfo map[string]*compiler.ConstInfo 180 } 181 182 func processJob(job *Job, descriptions *ast.Description, constFile *compiler.ConstFile) { 183 var flags []prog.FlagDesc 184 for _, decl := range descriptions.Nodes { 185 switch n := decl.(type) { 186 case *ast.IntFlags: 187 var flag prog.FlagDesc 188 flag.Name = n.Name.Name 189 for _, val := range n.Values { 190 flag.Values = append(flag.Values, val.Ident) 191 } 192 flags = append(flags, flag) 193 } 194 } 195 196 eh := func(pos ast.Pos, msg string) { 197 job.Errors = append(job.Errors, fmt.Sprintf("%v: %v\n", pos, msg)) 198 } 199 consts := constFile.Arch(job.Target.Arch) 200 prog := compiler.Compile(descriptions, consts, job.Target, eh) 201 if prog == nil { 202 return 203 } 204 for what := range prog.Unsupported { 205 job.Unsupported[what] = true 206 } 207 208 sysFile := filepath.Join(*outDir, "sys", job.Target.OS, "gen", job.Target.Arch+".go") 209 out := new(bytes.Buffer) 210 generate(job.Target, prog, consts, flags, out) 211 rev := hash.String(out.Bytes()) 212 fmt.Fprintf(out, "const revision_%v = %q\n", job.Target.Arch, rev) 213 writeSource(sysFile, out.Bytes()) 214 215 job.ArchData = generateExecutorSyscalls(job.Target, prog.Syscalls, rev) 216 217 // Don't print warnings, they are printed in syz-check. 218 job.Errors = nil 219 // But let's fail on always actionable errors. 220 if job.Target.OS != targets.Fuchsia { 221 // There are too many broken consts on Fuchsia. 222 constsAreAllDefined(constFile, job.ConstInfo, eh) 223 } 224 job.OK = len(job.Errors) == 0 225 } 226 227 func generate(target *targets.Target, prg *compiler.Prog, consts map[string]uint64, flags []prog.FlagDesc, 228 out io.Writer) { 229 tag := fmt.Sprintf("syz_target,syz_os_%v,syz_arch_%v", target.OS, target.Arch) 230 if target.VMArch != "" { 231 tag += fmt.Sprintf(" syz_target,syz_os_%v,syz_arch_%v", target.OS, target.VMArch) 232 } 233 fmt.Fprintf(out, "// AUTOGENERATED FILE\n") 234 fmt.Fprintf(out, "// +build !codeanalysis\n") 235 fmt.Fprintf(out, "// +build !syz_target %v\n\n", tag) 236 fmt.Fprintf(out, "package gen\n\n") 237 fmt.Fprintf(out, "import . \"github.com/google/syzkaller/prog\"\n") 238 fmt.Fprintf(out, "import . \"github.com/google/syzkaller/sys/%v\"\n\n", target.OS) 239 240 fmt.Fprintf(out, "func init() {\n") 241 fmt.Fprintf(out, "\tRegisterTarget(&Target{"+ 242 "OS: %q, Arch: %q, Revision: revision_%v, PtrSize: %v, PageSize: %v, "+ 243 "NumPages: %v, DataOffset: %v, LittleEndian: %v, ExecutorUsesShmem: %v, "+ 244 "Syscalls: syscalls_%v, Resources: resources_%v, Consts: consts_%v,"+ 245 "Flags: flags_%v}, types_%v, InitTarget)\n}\n\n", 246 target.OS, target.Arch, target.Arch, target.PtrSize, target.PageSize, 247 target.NumPages, target.DataOffset, target.LittleEndian, target.ExecutorUsesShmem, 248 target.Arch, target.Arch, target.Arch, target.Arch, target.Arch) 249 250 fmt.Fprintf(out, "var resources_%v = ", target.Arch) 251 serializer.Write(out, prg.Resources) 252 fmt.Fprintf(out, "\n\n") 253 254 fmt.Fprintf(out, "var syscalls_%v = ", target.Arch) 255 serializer.Write(out, prg.Syscalls) 256 fmt.Fprintf(out, "\n\n") 257 258 fmt.Fprintf(out, "var types_%v = ", target.Arch) 259 serializer.Write(out, prg.Types) 260 fmt.Fprintf(out, "\n\n") 261 262 fmt.Fprintf(out, "var flags_%v = ", target.Arch) 263 serializer.Write(out, flags) 264 fmt.Fprintf(out, "\n\n") 265 266 constArr := make([]prog.ConstValue, 0, len(consts)) 267 for name, val := range consts { 268 constArr = append(constArr, prog.ConstValue{Name: name, Value: val}) 269 } 270 sort.Slice(constArr, func(i, j int) bool { 271 return constArr[i].Name < constArr[j].Name 272 }) 273 fmt.Fprintf(out, "var consts_%v = ", target.Arch) 274 serializer.Write(out, constArr) 275 fmt.Fprintf(out, "\n\n") 276 } 277 278 func generateExecutorSyscalls(target *targets.Target, syscalls []*prog.Syscall, rev string) ArchData { 279 data := ArchData{ 280 Revision: rev, 281 GOARCH: target.Arch, 282 PageSize: target.PageSize, 283 NumPages: target.NumPages, 284 DataOffset: target.DataOffset, 285 } 286 if target.ExecutorUsesForkServer { 287 data.ForkServer = 1 288 } 289 if target.ExecutorUsesShmem { 290 data.Shmem = 1 291 } 292 defines := make(map[string]string) 293 for _, c := range syscalls { 294 var attrVals []uint64 295 attrs := reflect.ValueOf(c.Attrs) 296 last := -1 297 for i := 0; i < attrs.NumField(); i++ { 298 attr := attrs.Field(i) 299 val := uint64(0) 300 switch attr.Type().Kind() { 301 case reflect.Bool: 302 if attr.Bool() { 303 val = 1 304 } 305 case reflect.Uint64: 306 val = attr.Uint() 307 default: 308 panic("unsupported syscall attribute type") 309 } 310 attrVals = append(attrVals, val) 311 if val != 0 { 312 last = i 313 } 314 } 315 data.Calls = append(data.Calls, newSyscallData(target, c, attrVals[:last+1])) 316 // Some syscalls might not be present on the compiling machine, so we 317 // generate definitions for them. 318 if target.HasCallNumber(c.CallName) && target.NeedSyscallDefine(c.NR) { 319 defines[target.SyscallPrefix+c.CallName] = fmt.Sprintf("%d", c.NR) 320 } 321 } 322 sort.Slice(data.Calls, func(i, j int) bool { 323 return data.Calls[i].Name < data.Calls[j].Name 324 }) 325 // Get a sorted list of definitions. 326 defineNames := []string{} 327 for key := range defines { 328 defineNames = append(defineNames, key) 329 } 330 sort.Strings(defineNames) 331 for _, key := range defineNames { 332 data.Defines = append(data.Defines, Define{key, defines[key]}) 333 } 334 return data 335 } 336 337 func newSyscallData(target *targets.Target, sc *prog.Syscall, attrs []uint64) SyscallData { 338 callName, patchCallName := target.SyscallTrampolines[sc.Name] 339 if !patchCallName { 340 callName = sc.CallName 341 } 342 return SyscallData{ 343 Name: sc.Name, 344 CallName: callName, 345 NR: int32(sc.NR), 346 NeedCall: (!target.HasCallNumber(sc.CallName) || patchCallName) && 347 // These are declared in the compiler for internal purposes. 348 !strings.HasPrefix(sc.Name, "syz_builtin"), 349 Attrs: attrs, 350 } 351 } 352 353 func writeExecutorSyscalls(data *ExecutorData) { 354 osutil.MkdirAll(filepath.Join(*outDir, "executor")) 355 sort.Slice(data.OSes, func(i, j int) bool { 356 return data.OSes[i].GOOS < data.OSes[j].GOOS 357 }) 358 buf := new(bytes.Buffer) 359 if err := defsTempl.Execute(buf, data); err != nil { 360 tool.Failf("failed to execute defs template: %v", err) 361 } 362 writeFile(filepath.Join(*outDir, "executor", "defs.h"), buf.Bytes()) 363 buf.Reset() 364 if err := syscallsTempl.Execute(buf, data); err != nil { 365 tool.Failf("failed to execute syscalls template: %v", err) 366 } 367 writeFile(filepath.Join(*outDir, "executor", "syscalls.h"), buf.Bytes()) 368 } 369 370 func writeSource(file string, data []byte) { 371 if oldSrc, err := os.ReadFile(file); err == nil && bytes.Equal(data, oldSrc) { 372 return 373 } 374 writeFile(file, data) 375 } 376 377 func writeFile(file string, data []byte) { 378 outf, err := os.Create(file) 379 if err != nil { 380 tool.Failf("failed to create output file: %v", err) 381 } 382 defer outf.Close() 383 outf.Write(data) 384 } 385 386 var defsTempl = template.Must(template.New("").Parse(`// AUTOGENERATED FILE 387 388 struct call_attrs_t { {{range $attr := $.CallAttrs}} 389 uint64_t {{$attr}};{{end}} 390 }; 391 392 struct call_props_t { {{range $attr := $.CallProps}} 393 {{$attr.Type}} {{$attr.Name}};{{end}} 394 }; 395 396 #define read_call_props_t(var, reader) { \{{range $attr := $.CallProps}} 397 (var).{{$attr.Name}} = ({{$attr.Type}})(reader); \{{end}} 398 } 399 400 {{range $os := $.OSes}} 401 #if GOOS_{{$os.GOOS}} 402 #define GOOS "{{$os.GOOS}}" 403 {{range $arch := $os.Archs}} 404 #if GOARCH_{{$arch.GOARCH}} 405 #define GOARCH "{{.GOARCH}}" 406 #define SYZ_REVISION "{{.Revision}}" 407 #define SYZ_EXECUTOR_USES_FORK_SERVER {{.ForkServer}} 408 #define SYZ_EXECUTOR_USES_SHMEM {{.Shmem}} 409 #define SYZ_PAGE_SIZE {{.PageSize}} 410 #define SYZ_NUM_PAGES {{.NumPages}} 411 #define SYZ_DATA_OFFSET {{.DataOffset}} 412 {{range $c := $arch.Defines}}#ifndef {{$c.Name}} 413 #define {{$c.Name}} {{$c.Value}} 414 #endif 415 {{end}}#endif 416 {{end}} 417 #endif 418 {{end}} 419 `)) 420 421 // nolint: lll 422 var syscallsTempl = template.Must(template.New("").Parse(`// AUTOGENERATED FILE 423 // clang-format off 424 {{range $os := $.OSes}} 425 #if GOOS_{{$os.GOOS}} 426 {{range $arch := $os.Archs}} 427 #if GOARCH_{{$arch.GOARCH}} 428 const call_t syscalls[] = { 429 {{range $c := $arch.Calls}} {"{{$c.Name}}", {{$c.NR}}{{if or $c.Attrs $c.NeedCall}}, { {{- range $attr := $c.Attrs}}{{$attr}}, {{end}}}{{end}}{{if $c.NeedCall}}, (syscall_t){{$c.CallName}}{{end}}}, 430 {{end}}}; 431 #endif 432 {{end}} 433 #endif 434 {{end}} 435 `))