From 6828dc3a613eab724840ba670955f4eaca70ab12 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Sat, 17 Jun 2017 13:28:22 +0200 Subject: sys/syz-sysgen: from from sysgen --- Makefile | 2 +- sys/syz-sysgen/syscallnr.go | 103 +++++ sys/syz-sysgen/sysgen.go | 887 ++++++++++++++++++++++++++++++++++++++++++++ sysgen/syscallnr.go | 103 ----- sysgen/sysgen.go | 887 -------------------------------------------- 5 files changed, 991 insertions(+), 991 deletions(-) create mode 100644 sys/syz-sysgen/syscallnr.go create mode 100644 sys/syz-sysgen/sysgen.go delete mode 100644 sysgen/syscallnr.go delete mode 100644 sysgen/sysgen.go diff --git a/Makefile b/Makefile index 3a7d6551b..d50ad073b 100644 --- a/Makefile +++ b/Makefile @@ -64,7 +64,7 @@ generate: bin/syz-sysgen go generate ./pkg/csource ./executor ./syz-gce ./pkg/ifuzz ./pkg/kernel $(MAKE) format bin/syz-sysgen: - go build $(GOFLAGS) -o $@ ./sysgen + go build $(GOFLAGS) -o $@ ./sys/syz-sysgen format: go fmt ./... diff --git a/sys/syz-sysgen/syscallnr.go b/sys/syz-sysgen/syscallnr.go new file mode 100644 index 000000000..0b41a8634 --- /dev/null +++ b/sys/syz-sysgen/syscallnr.go @@ -0,0 +1,103 @@ +// Copyright 2015 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "sort" + "text/template" + + . "github.com/google/syzkaller/sys/sysparser" +) + +type Arch struct { + Name string + CARCH []string +} + +var archs = []*Arch{ + {"amd64", []string{"__x86_64__"}}, + {"arm64", []string{"__aarch64__"}}, + {"ppc64le", []string{"__ppc64__", "__PPC64__", "__powerpc64__"}}, +} + +var syzkalls = map[string]uint64{ + "syz_test": 1000001, + "syz_open_dev": 1000002, + "syz_open_pts": 1000003, + "syz_fuse_mount": 1000004, + "syz_fuseblk_mount": 1000005, + "syz_emit_ethernet": 1000006, + "syz_kvm_setup_cpu": 1000007, + "syz_extract_tcp_res": 1000008, +} + +func generateExecutorSyscalls(syscalls []Syscall, consts map[string]map[string]uint64) { + var data SyscallsData + for _, arch := range archs { + var calls []SyscallData + for _, c := range syscalls { + syscallNR := -1 + if nr, ok := consts[arch.Name]["__NR_"+c.CallName]; ok { + syscallNR = int(nr) + } + calls = append(calls, SyscallData{c.Name, syscallNR}) + } + data.Archs = append(data.Archs, ArchData{arch.CARCH, calls}) + } + for name, nr := range syzkalls { + data.FakeCalls = append(data.FakeCalls, SyscallData{name, int(nr)}) + } + sort.Sort(SyscallArray(data.FakeCalls)) + + hdrcode := "executor/syscalls.h" + logf(1, "Generate header with syscall numbers in %v", hdrcode) + buf := new(bytes.Buffer) + if err := syscallsTempl.Execute(buf, data); err != nil { + failf("failed to execute syscalls template: %v", err) + } + writeFile(hdrcode, buf.Bytes()) +} + +type SyscallsData struct { + Archs []ArchData + FakeCalls []SyscallData +} + +type ArchData struct { + CARCH []string + Calls []SyscallData +} + +type SyscallData struct { + Name string + NR int +} + +type SyscallArray []SyscallData + +func (a SyscallArray) Len() int { return len(a) } +func (a SyscallArray) Less(i, j int) bool { return a[i].Name < a[j].Name } +func (a SyscallArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +var syscallsTempl = template.Must(template.New("").Parse( + `// AUTOGENERATED FILE + +{{range $c := $.FakeCalls}}#define __NR_{{$c.Name}} {{$c.NR}} +{{end}} + +struct call_t { + const char* name; + int sys_nr; +}; + +{{range $arch := $.Archs}} +#if {{range $cdef := $arch.CARCH}}defined({{$cdef}}) || {{end}}0 +static call_t syscalls[] = { +{{range $c := $arch.Calls}} {"{{$c.Name}}", {{$c.NR}}}, +{{end}} +}; +#endif +{{end}} +`)) diff --git a/sys/syz-sysgen/sysgen.go b/sys/syz-sysgen/sysgen.go new file mode 100644 index 000000000..c1a6caf27 --- /dev/null +++ b/sys/syz-sysgen/sysgen.go @@ -0,0 +1,887 @@ +// Copyright 2015/2016 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package main + +import ( + "bufio" + "bytes" + "flag" + "fmt" + "go/format" + "io" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "runtime" + "runtime/pprof" + "sort" + "strconv" + "strings" + + . "github.com/google/syzkaller/sys/sysparser" +) + +var ( + flagV = flag.Int("v", 0, "verbosity") + flagMemProfile = flag.String("memprofile", "", "write a memory profile to the file") + + intRegExp = regexp.MustCompile("^int([0-9]+|ptr)(be)?(:[0-9]+)?$") +) + +const ( + ptrSize = 8 +) + +func main() { + flag.Parse() + + inputFiles, err := filepath.Glob("sys/*\\.txt") + if err != nil { + failf("failed to find input files: %v", err) + } + var r io.Reader = bytes.NewReader(nil) + for _, f := range inputFiles { + inf, err := os.Open(f) + logf(1, "Load descriptions from file %v", f) + if err != nil { + failf("failed to open input file: %v", err) + } + defer inf.Close() + r = io.MultiReader(r, bufio.NewReader(inf)) + } + + logf(1, "Parse system call descriptions") + desc := Parse(r) + + consts := make(map[string]map[string]uint64) + for _, arch := range archs { + logf(0, "generating %v...", arch.Name) + consts[arch.Name] = readConsts(arch.Name) + + unsupported := make(map[string]bool) + archFlags := make(map[string][]string) + for f, vals := range desc.Flags { + var archVals []string + for _, val := range vals { + if isIdentifier(val) { + if v, ok := consts[arch.Name][val]; ok { + archVals = append(archVals, fmt.Sprint(v)) + } else { + if !unsupported[val] { + unsupported[val] = true + logf(0, "unsupported flag: %v", val) + } + } + } else { + archVals = append(archVals, val) + } + } + archFlags[f] = archVals + } + + sysFile := filepath.Join("sys", "sys_"+arch.Name+".go") + logf(1, "Generate code to init system call data in %v", sysFile) + out := new(bytes.Buffer) + archDesc := *desc + archDesc.Flags = archFlags + generate(arch.Name, &archDesc, consts[arch.Name], out) + writeSource(sysFile, out.Bytes()) + logf(0, "") + } + + generateExecutorSyscalls(desc.Syscalls, consts) + + if *flagMemProfile != "" { + f, err := os.Create(*flagMemProfile) + if err != nil { + failf("could not create memory profile: ", err) + } + runtime.GC() // get up-to-date statistics + if err := pprof.WriteHeapProfile(f); err != nil { + failf("could not write memory profile: ", err) + } + f.Close() + } +} + +func readConsts(arch string) map[string]uint64 { + constFiles, err := filepath.Glob("sys/*_" + arch + ".const") + if err != nil { + failf("failed to find const files: %v", err) + } + consts := make(map[string]uint64) + for _, fname := range constFiles { + f, err := os.Open(fname) + if err != nil { + failf("failed to open const file: %v", err) + } + defer f.Close() + s := bufio.NewScanner(f) + for s.Scan() { + line := s.Text() + if line == "" || line[0] == '#' { + continue + } + eq := strings.IndexByte(line, '=') + if eq == -1 { + failf("malformed const file %v: no '=' in '%v'", fname, line) + } + name := strings.TrimSpace(line[:eq]) + val, err := strconv.ParseUint(strings.TrimSpace(line[eq+1:]), 0, 64) + if err != nil { + failf("malformed const file %v: bad value in '%v'", fname, line) + } + if old, ok := consts[name]; ok && old != val { + failf("const %v has different values for %v: %v vs %v", name, arch, old, val) + } + consts[name] = val + } + if err := s.Err(); err != nil { + failf("failed to read const file: %v", err) + } + } + for name, nr := range syzkalls { + consts["__NR_"+name] = nr + } + return consts +} + +var skipCurrentSyscall string + +func skipSyscall(why string) { + if skipCurrentSyscall != "" { + skipCurrentSyscall = why + } +} + +func generate(arch string, desc *Description, consts map[string]uint64, out io.Writer) { + unsupported := make(map[string]bool) + + fmt.Fprintf(out, "// AUTOGENERATED FILE\n") + fmt.Fprintf(out, "package sys\n\n") + + generateResources(desc, consts, out) + generateStructs(desc, consts, out) + + fmt.Fprintf(out, "var Calls = []*Call{\n") + for _, s := range desc.Syscalls { + logf(4, " generate population code for %v", s.Name) + skipCurrentSyscall = "" + syscallNR := -1 + if nr, ok := consts["__NR_"+s.CallName]; ok { + syscallNR = int(nr) + } else { + if !unsupported[s.CallName] { + unsupported[s.CallName] = true + logf(0, "unsupported syscall: %v", s.CallName) + } + } + native := true + if _, ok := syzkalls[s.CallName]; ok { + native = false + } + fmt.Fprintf(out, "&Call{Name: \"%v\", CallName: \"%v\", Native: %v", s.Name, s.CallName, native) + if len(s.Ret) != 0 { + fmt.Fprintf(out, ", Ret: ") + generateArg("", "ret", s.Ret[0], "out", s.Ret[1:], desc, consts, true, false, out) + } + fmt.Fprintf(out, ", Args: []Type{") + for i, a := range s.Args { + if i != 0 { + fmt.Fprintf(out, ", ") + } + logf(5, " generate description for arg %v", i) + generateArg("", a[0], a[1], "in", a[2:], desc, consts, true, false, out) + } + if skipCurrentSyscall != "" { + logf(0, "unsupported syscall: %v due to %v", s.Name, skipCurrentSyscall) + syscallNR = -1 + } + fmt.Fprintf(out, "}, NR: %v},\n", syscallNR) + } + fmt.Fprintf(out, "}\n\n") + + var constArr []NameValue + for name, val := range consts { + constArr = append(constArr, NameValue{name, val}) + } + sort.Sort(NameValueArray(constArr)) + + fmt.Fprintf(out, "const (\n") + for _, nv := range constArr { + fmt.Fprintf(out, "%v = %v\n", nv.name, nv.val) + } + fmt.Fprintf(out, ")\n") +} + +func generateResources(desc *Description, consts map[string]uint64, out io.Writer) { + var resArray ResourceArray + for _, res := range desc.Resources { + resArray = append(resArray, res) + } + sort.Sort(resArray) + + fmt.Fprintf(out, "var resourceArray = []*ResourceDesc{\n") + for _, res := range resArray { + underlying := "" + name := res.Name + kind := []string{name} + var values []string + loop: + for { + var values1 []string + for _, v := range res.Values { + if v1, ok := consts[v]; ok { + values1 = append(values1, fmt.Sprint(v1)) + } else if !isIdentifier(v) { + values1 = append(values1, v) + } + } + values = append(values1, values...) + switch res.Base { + case "int8", "int16", "int32", "int64", "intptr": + underlying = res.Base + break loop + default: + if _, ok := desc.Resources[res.Base]; !ok { + failf("resource '%v' has unknown parent resource '%v'", name, res.Base) + } + kind = append([]string{res.Base}, kind...) + res = desc.Resources[res.Base] + } + } + fmt.Fprintf(out, "&ResourceDesc{Name: \"%v\", Type: ", name) + generateArg("", "resource-type", underlying, "inout", nil, desc, consts, true, true, out) + fmt.Fprintf(out, ", Kind: []string{") + for i, k := range kind { + if i != 0 { + fmt.Fprintf(out, ", ") + } + fmt.Fprintf(out, "\"%v\"", k) + } + fmt.Fprintf(out, "}, Values: []uintptr{") + if len(values) == 0 { + values = append(values, "0") + } + for i, v := range values { + if i != 0 { + fmt.Fprintf(out, ", ") + } + fmt.Fprintf(out, "%v", v) + } + fmt.Fprintf(out, "}},\n") + } + fmt.Fprintf(out, "}\n") +} + +type structKey struct { + name string + field string + dir string +} + +func generateStructEntry(str *Struct, out io.Writer) { + typ := "StructType" + if str.IsUnion { + typ = "UnionType" + } + packed := "" + if str.Packed { + packed = ", packed: true" + } + varlen := "" + if str.Varlen { + varlen = ", varlen: true" + } + align := "" + if str.Align != 0 { + align = fmt.Sprintf(", align: %v", str.Align) + } + fmt.Fprintf(out, "&%v{TypeCommon: TypeCommon{TypeName: \"%v\", IsOptional: %v} %v %v %v},\n", + typ, str.Name, false, packed, align, varlen) +} + +func generateStructFields(str *Struct, key structKey, desc *Description, consts map[string]uint64, out io.Writer) { + fmt.Fprintf(out, "{structKey{\"%v\", \"%v\", %v}, []Type{\n", key.name, key.field, fmtDir(key.dir)) + for _, a := range str.Flds { + generateArg(str.Name, a[0], a[1], key.dir, a[2:], desc, consts, false, true, out) + fmt.Fprintf(out, ",\n") + } + fmt.Fprintf(out, "}},\n") + +} + +func generateStructs(desc *Description, consts map[string]uint64, out io.Writer) { + // Struct fields can refer to other structs. Go compiler won't like if + // we refer to Structs during Structs initialization. So we do + // it in 2 passes: on the first pass create struct types without fields, + // on the second pass we fill in fields. + + // Since structs of the same type can be fields with different names + // of multiple other structs, we have an instance of those structs + // for each field indexed by the name of the parent struct, field name and dir. + + structMap := make(map[structKey]*Struct) + for _, str := range desc.Structs { + for _, dir := range []string{"in", "out", "inout"} { + structMap[structKey{str.Name, "", dir}] = str + } + for _, a := range str.Flds { + if innerStr, ok := desc.Structs[a[1]]; ok { + for _, dir := range []string{"in", "out", "inout"} { + structMap[structKey{a[1], a[0], dir}] = innerStr + } + } + } + } + + fmt.Fprintf(out, "var structArray = []Type{\n") + sortedStructs := make([]*Struct, 0, len(desc.Structs)) + for _, str := range desc.Structs { + sortedStructs = append(sortedStructs, str) + } + sort.Sort(structSorter(sortedStructs)) + for _, str := range sortedStructs { + generateStructEntry(str, out) + } + fmt.Fprintf(out, "}\n") + + fmt.Fprintf(out, "var structFields = []struct{key structKey; fields []Type}{") + sortedKeys := make([]structKey, 0, len(structMap)) + for key := range structMap { + sortedKeys = append(sortedKeys, key) + } + sort.Sort(structKeySorter(sortedKeys)) + for _, key := range sortedKeys { + generateStructFields(structMap[key], key, desc, consts, out) + } + fmt.Fprintf(out, "}\n") +} + +func parseRange(buffer string, consts map[string]uint64) (string, string) { + lookupConst := func(name string) string { + if v, ok := consts[name]; ok { + return fmt.Sprint(v) + } + return name + } + + parts := strings.Split(buffer, ":") + switch len(parts) { + case 1: + v := lookupConst(buffer) + return v, v + case 2: + return lookupConst(parts[0]), lookupConst(parts[1]) + default: + failf("bad range: %v", buffer) + return "", "" + } +} + +func generateArg( + parent, name, typ, dir string, + a []string, + desc *Description, + consts map[string]uint64, + isArg, isField bool, + out io.Writer) { + origName := name + name = "\"" + name + "\"" + opt := false + for i, v := range a { + if v == "opt" { + opt = true + copy(a[i:], a[i+1:]) + a = a[:len(a)-1] + break + } + } + fmtDir(dir) // Make sure that dir is "in", "out" or "inout" + common := func() string { + return fmt.Sprintf("TypeCommon: TypeCommon{TypeName: \"%v\", FldName: %v, ArgDir: %v, IsOptional: %v}", typ, name, fmtDir(dir), opt) + } + intCommon := func(typeSize uint64, bigEndian bool, bitfieldLen uint64) string { + // BitfieldOff and BitfieldLst will be filled in in initAlign(). + return fmt.Sprintf("IntTypeCommon: IntTypeCommon{%v, TypeSize: %v, BigEndian: %v, BitfieldLen: %v}", common(), typeSize, bigEndian, bitfieldLen) + } + canBeArg := false + switch typ { + case "fileoff": + canBeArg = true + size := uint64(ptrSize) + bigEndian := false + bitfieldLen := uint64(0) + if isField { + if want := 1; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + size, bigEndian, bitfieldLen = decodeIntType(a[0]) + } else { + if want := 0; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + } + fmt.Fprintf(out, "&IntType{%v, Kind: IntFileoff}", intCommon(size, bigEndian, bitfieldLen)) + case "buffer": + canBeArg = true + if want := 1; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + ptrCommonHdr := common() + dir = a[0] + opt = false + fmt.Fprintf(out, "&PtrType{%v, Type: &BufferType{%v, Kind: BufferBlobRand}}", ptrCommonHdr, common()) + case "string": + if len(a) != 0 && len(a) != 1 && len(a) != 2 { + failf("wrong number of arguments for %v arg %v, want 0-2, got %v", typ, name, len(a)) + } + var vals []string + subkind := "" + if len(a) >= 1 { + if a[0][0] == '"' { + vals = append(vals, a[0][1:len(a[0])-1]) + } else { + vals1, ok := desc.StrFlags[a[0]] + if !ok { + failf("unknown string flags %v", a[0]) + } + vals = append([]string{}, vals1...) + subkind = a[0] + } + } + for i, s := range vals { + vals[i] = s + "\x00" + } + var size uint64 + if len(a) >= 2 { + if v, ok := consts[a[1]]; ok { + size = v + } else { + v, err := strconv.ParseUint(a[1], 10, 64) + if err != nil { + failf("failed to parse string length for %v", name, a[1]) + } + size = v + } + for i, s := range vals { + if uint64(len(s)) > size { + failf("string value %q exceeds buffer length %v for arg %v", s, size, name) + } + for uint64(len(s)) < size { + s += "\x00" + } + vals[i] = s + } + } else { + for _, s := range vals { + if size != 0 && size != uint64(len(s)) { + size = 0 + break + } + size = uint64(len(s)) + } + } + fmt.Fprintf(out, "&BufferType{%v, Kind: BufferString, SubKind: %q, Values: %#v, Length: %v}", common(), subkind, vals, size) + case "salg_type": + if want := 0; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + fmt.Fprintf(out, "&BufferType{%v, Kind: BufferAlgType}", common()) + case "salg_name": + if want := 0; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + fmt.Fprintf(out, "&BufferType{%v, Kind: BufferAlgName}", common()) + case "vma": + canBeArg = true + begin, end := "0", "0" + switch len(a) { + case 0: + case 1: + begin, end = parseRange(a[0], consts) + default: + failf("wrong number of arguments for %v arg %v, want 0 or 1, got %v", typ, name, len(a)) + } + fmt.Fprintf(out, "&VmaType{%v, RangeBegin: %v, RangeEnd: %v}", common(), begin, end) + case "len", "bytesize", "bytesize2", "bytesize4", "bytesize8": + canBeArg = true + size := uint64(ptrSize) + bigEndian := false + bitfieldLen := uint64(0) + if isField { + if want := 2; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + size, bigEndian, bitfieldLen = decodeIntType(a[1]) + } else { + if want := 1; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + } + byteSize := uint8(0) + if typ != "len" { + byteSize = decodeByteSizeType(typ) + } + fmt.Fprintf(out, "&LenType{%v, Buf: \"%v\", ByteSize: %v}", intCommon(size, bigEndian, bitfieldLen), a[0], byteSize) + case "csum": + if len(a) != 3 && len(a) != 4 { + failf("wrong number of arguments for %v arg %v, want 3-4, got %v", typ, name, len(a)) + } + var size uint64 + var bigEndian bool + var bitfieldLen uint64 + var protocol uint64 + var kind string + switch a[1] { + case "inet": + kind = "CsumInet" + size, bigEndian, bitfieldLen = decodeIntType(a[2]) + case "pseudo": + kind = "CsumPseudo" + size, bigEndian, bitfieldLen = decodeIntType(a[3]) + if v, ok := consts[a[2]]; ok { + protocol = v + } else { + v, err := strconv.ParseUint(a[2], 10, 64) + if err != nil { + failf("failed to parse protocol %v", a[2]) + } + protocol = v + } + default: + failf("unknown checksum kind '%v'", a[0]) + } + fmt.Fprintf(out, "&CsumType{%v, Buf: \"%s\", Kind: %v, Protocol: %v}", intCommon(size, bigEndian, bitfieldLen), a[0], kind, protocol) + case "flags": + canBeArg = true + size := uint64(ptrSize) + bigEndian := false + bitfieldLen := uint64(0) + if isField { + if want := 2; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + size, bigEndian, bitfieldLen = decodeIntType(a[1]) + } else { + if want := 1; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + } + vals, ok := desc.Flags[a[0]] + if !ok { + failf("unknown flag %v", a[0]) + } + if len(vals) == 0 { + fmt.Fprintf(out, "&IntType{%v}", intCommon(size, bigEndian, bitfieldLen)) + } else { + fmt.Fprintf(out, "&FlagsType{%v, Vals: []uintptr{%v}}", intCommon(size, bigEndian, bitfieldLen), strings.Join(vals, ",")) + } + case "const": + canBeArg = true + size := uint64(ptrSize) + bigEndian := false + bitfieldLen := uint64(0) + if isField { + if want := 2; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + size, bigEndian, bitfieldLen = decodeIntType(a[1]) + } else { + if want := 1; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + } + val := a[0] + if v, ok := consts[a[0]]; ok { + val = fmt.Sprint(v) + } else if isIdentifier(a[0]) { + // This is an identifier for which we don't have a value for this arch. + // Skip this syscall on this arch. + val = "0" + skipSyscall(fmt.Sprintf("missing const %v", a[0])) + } + fmt.Fprintf(out, "&ConstType{%v, Val: uintptr(%v)}", intCommon(size, bigEndian, bitfieldLen), val) + case "proc": + canBeArg = true + size := uint64(ptrSize) + bigEndian := false + bitfieldLen := uint64(0) + var valuesStart string + var valuesPerProc string + if isField { + if want := 3; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + size, bigEndian, bitfieldLen = decodeIntType(a[0]) + valuesStart = a[1] + valuesPerProc = a[2] + } else { + if want := 2; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + valuesStart = a[0] + valuesPerProc = a[1] + } + valuesStartInt, err := strconv.ParseInt(valuesStart, 10, 64) + if err != nil { + failf("couldn't parse '%v' as int64", valuesStart) + } + valuesPerProcInt, err := strconv.ParseInt(valuesPerProc, 10, 64) + if err != nil { + failf("couldn't parse '%v' as int64", valuesPerProc) + } + if valuesPerProcInt < 1 { + failf("values per proc '%v' should be >= 1", valuesPerProcInt) + } + if size != 8 && valuesStartInt >= (1<<(size*8)) { + failf("values starting from '%v' overflow desired type of size '%v'", valuesStartInt, size) + } + const maxPids = 32 // executor knows about this constant (MAX_PIDS) + if size != 8 && valuesStartInt+maxPids*valuesPerProcInt >= (1<<(size*8)) { + failf("not enough values starting from '%v' with step '%v' and type size '%v' for 32 procs", valuesStartInt, valuesPerProcInt, size) + } + fmt.Fprintf(out, "&ProcType{%v, ValuesStart: %v, ValuesPerProc: %v}", intCommon(size, bigEndian, bitfieldLen), valuesStartInt, valuesPerProcInt) + case "signalno": + canBeArg = true + if want := 0; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + fmt.Fprintf(out, "&IntType{%v, Kind: IntSignalno}", intCommon(4, false, 0)) + case "filename": + if want := 0; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + fmt.Fprintf(out, "&BufferType{%v, Kind: BufferFilename}", common()) + case "text": + if want := 1; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + kind := "" + switch a[0] { + case "x86_real", "x86_16", "x86_32", "x86_64", "arm64": + kind = "Text_" + a[0] + default: + failf("unknown text type %v for %v arg %v", a[0], typ, name) + } + fmt.Fprintf(out, "&BufferType{%v, Kind: BufferText, Text: %v}", common(), kind) + case "array": + if len(a) != 1 && len(a) != 2 { + failf("wrong number of arguments for %v arg %v, want 1 or 2, got %v", typ, name, len(a)) + } + if len(a) == 1 { + if a[0] == "int8" { + fmt.Fprintf(out, "&BufferType{%v, Kind: BufferBlobRand}", common()) + } else { + fmt.Fprintf(out, "&ArrayType{%v, Type: %v, Kind: ArrayRandLen}", common(), generateType(a[0], dir, desc, consts)) + } + } else { + begin, end := parseRange(a[1], consts) + if a[0] == "int8" { + fmt.Fprintf(out, "&BufferType{%v, Kind: BufferBlobRange, RangeBegin: %v, RangeEnd: %v}", common(), begin, end) + } else { + fmt.Fprintf(out, "&ArrayType{%v, Type: %v, Kind: ArrayRangeLen, RangeBegin: %v, RangeEnd: %v}", common(), generateType(a[0], dir, desc, consts), begin, end) + } + } + case "ptr": + canBeArg = true + if want := 2; len(a) != want { + failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) + } + dir = "in" + fmt.Fprintf(out, "&PtrType{%v, Type: %v}", common(), generateType(a[1], a[0], desc, consts)) + default: + if intRegExp.MatchString(typ) { + canBeArg = true + size, bigEndian, bitfieldLen := decodeIntType(typ) + switch len(a) { + case 0: + fmt.Fprintf(out, "&IntType{%v}", intCommon(size, bigEndian, bitfieldLen)) + case 1: + begin, end := parseRange(a[0], consts) + fmt.Fprintf(out, "&IntType{%v, Kind: IntRange, RangeBegin: %v, RangeEnd: %v}", + intCommon(size, bigEndian, bitfieldLen), begin, end) + default: + failf("wrong number of arguments for %v arg %v, want 0 or 1, got %v", typ, name, len(a)) + } + } else if strings.HasPrefix(typ, "unnamed") { + if inner, ok := desc.Unnamed[typ]; ok { + generateArg("", "", inner[0], dir, inner[1:], desc, consts, false, isField, out) + } else { + failf("unknown unnamed type '%v'", typ) + } + } else if _, ok := desc.Structs[typ]; ok { + if len(a) != 0 { + failf("struct '%v' has args", typ) + } + fmt.Fprintf(out, "getStruct(structKey{\"%v\", \"%v\", %v})", typ, origName, fmtDir(dir)) + } else if _, ok := desc.Resources[typ]; ok { + if len(a) != 0 { + failf("resource '%v' has args", typ) + } + fmt.Fprintf(out, "&ResourceType{%v, Desc: resource(\"%v\")}", common(), typ) + return + } else { + failf("unknown arg type \"%v\" for %v", typ, name) + } + } + if isArg && !canBeArg { + failf("%v %v can't be syscall argument/return", name, typ) + } +} + +func generateType(typ, dir string, desc *Description, consts map[string]uint64) string { + buf := new(bytes.Buffer) + generateArg("", "", typ, dir, nil, desc, consts, false, true, buf) + return buf.String() +} + +func fmtDir(s string) string { + switch s { + case "in": + return "DirIn" + case "out": + return "DirOut" + case "inout": + return "DirInOut" + default: + failf("bad direction %v", s) + return "" + } +} + +func decodeIntType(typ string) (uint64, bool, uint64) { + bigEndian := false + bitfieldLen := uint64(0) + + parts := strings.Split(typ, ":") + if len(parts) == 2 { + var err error + bitfieldLen, err = strconv.ParseUint(parts[1], 10, 64) + if err != nil { + failf("failed to parse bitfield length '%v'", parts[1]) + } + typ = parts[0] + } + + if strings.HasSuffix(typ, "be") { + bigEndian = true + typ = typ[:len(typ)-2] + } + + switch typ { + case "int8", "int16", "int32", "int64", "intptr": + default: + failf("unknown type %v", typ) + } + sz := int64(ptrSize * 8) + if typ != "intptr" { + sz, _ = strconv.ParseInt(typ[3:], 10, 64) + } + + if bitfieldLen > uint64(sz) { + failf("bitfield of size %v is too large for base type of size %v", bitfieldLen, sz/8) + } + + return uint64(sz / 8), bigEndian, bitfieldLen +} + +func decodeByteSizeType(typ string) uint8 { + switch typ { + case "bytesize", "bytesize2", "bytesize4", "bytesize8": + default: + failf("unknown type %v", typ) + } + sz := int64(1) + if typ != "bytesize" { + sz, _ = strconv.ParseInt(typ[8:], 10, 8) + } + return uint8(sz) +} + +func isIdentifier(s string) bool { + for i, c := range s { + if c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || i > 0 && (c >= '0' && c <= '9') { + continue + } + return false + } + return true +} + +func writeSource(file string, data []byte) { + src, err := format.Source(data) + if err != nil { + fmt.Printf("%s\n", data) + failf("failed to format output: %v", err) + } + if oldSrc, err := ioutil.ReadFile(file); err == nil && bytes.Equal(src, oldSrc) { + return + } + writeFile(file, src) +} + +func writeFile(file string, data []byte) { + outf, err := os.Create(file) + if err != nil { + failf("failed to create output file: %v", err) + } + defer outf.Close() + outf.Write(data) +} + +type NameValue struct { + name string + val uint64 +} + +type NameValueArray []NameValue + +func (a NameValueArray) Len() int { return len(a) } +func (a NameValueArray) Less(i, j int) bool { return a[i].name < a[j].name } +func (a NameValueArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +type ResourceArray []Resource + +func (a ResourceArray) Len() int { return len(a) } +func (a ResourceArray) Less(i, j int) bool { return a[i].Name < a[j].Name } +func (a ResourceArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +type structSorter []*Struct + +func (a structSorter) Len() int { return len(a) } +func (a structSorter) Less(i, j int) bool { return a[i].Name < a[j].Name } +func (a structSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +type structKeySorter []structKey + +func (a structKeySorter) Len() int { return len(a) } +func (a structKeySorter) Less(i, j int) bool { + if a[i].name < a[j].name { + return true + } + if a[i].name > a[j].name { + return false + } + if a[i].field < a[j].field { + return true + } + if a[i].field > a[j].field { + return false + } + return a[i].dir < a[j].dir +} +func (a structKeySorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +func failf(msg string, args ...interface{}) { + fmt.Fprintf(os.Stderr, msg+"\n", args...) + os.Exit(1) +} + +func logf(v int, msg string, args ...interface{}) { + if *flagV >= v { + fmt.Fprintf(os.Stderr, msg+"\n", args...) + } +} diff --git a/sysgen/syscallnr.go b/sysgen/syscallnr.go deleted file mode 100644 index 0b41a8634..000000000 --- a/sysgen/syscallnr.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2015 syzkaller project authors. All rights reserved. -// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. - -package main - -import ( - "bytes" - "sort" - "text/template" - - . "github.com/google/syzkaller/sys/sysparser" -) - -type Arch struct { - Name string - CARCH []string -} - -var archs = []*Arch{ - {"amd64", []string{"__x86_64__"}}, - {"arm64", []string{"__aarch64__"}}, - {"ppc64le", []string{"__ppc64__", "__PPC64__", "__powerpc64__"}}, -} - -var syzkalls = map[string]uint64{ - "syz_test": 1000001, - "syz_open_dev": 1000002, - "syz_open_pts": 1000003, - "syz_fuse_mount": 1000004, - "syz_fuseblk_mount": 1000005, - "syz_emit_ethernet": 1000006, - "syz_kvm_setup_cpu": 1000007, - "syz_extract_tcp_res": 1000008, -} - -func generateExecutorSyscalls(syscalls []Syscall, consts map[string]map[string]uint64) { - var data SyscallsData - for _, arch := range archs { - var calls []SyscallData - for _, c := range syscalls { - syscallNR := -1 - if nr, ok := consts[arch.Name]["__NR_"+c.CallName]; ok { - syscallNR = int(nr) - } - calls = append(calls, SyscallData{c.Name, syscallNR}) - } - data.Archs = append(data.Archs, ArchData{arch.CARCH, calls}) - } - for name, nr := range syzkalls { - data.FakeCalls = append(data.FakeCalls, SyscallData{name, int(nr)}) - } - sort.Sort(SyscallArray(data.FakeCalls)) - - hdrcode := "executor/syscalls.h" - logf(1, "Generate header with syscall numbers in %v", hdrcode) - buf := new(bytes.Buffer) - if err := syscallsTempl.Execute(buf, data); err != nil { - failf("failed to execute syscalls template: %v", err) - } - writeFile(hdrcode, buf.Bytes()) -} - -type SyscallsData struct { - Archs []ArchData - FakeCalls []SyscallData -} - -type ArchData struct { - CARCH []string - Calls []SyscallData -} - -type SyscallData struct { - Name string - NR int -} - -type SyscallArray []SyscallData - -func (a SyscallArray) Len() int { return len(a) } -func (a SyscallArray) Less(i, j int) bool { return a[i].Name < a[j].Name } -func (a SyscallArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -var syscallsTempl = template.Must(template.New("").Parse( - `// AUTOGENERATED FILE - -{{range $c := $.FakeCalls}}#define __NR_{{$c.Name}} {{$c.NR}} -{{end}} - -struct call_t { - const char* name; - int sys_nr; -}; - -{{range $arch := $.Archs}} -#if {{range $cdef := $arch.CARCH}}defined({{$cdef}}) || {{end}}0 -static call_t syscalls[] = { -{{range $c := $arch.Calls}} {"{{$c.Name}}", {{$c.NR}}}, -{{end}} -}; -#endif -{{end}} -`)) diff --git a/sysgen/sysgen.go b/sysgen/sysgen.go deleted file mode 100644 index c1a6caf27..000000000 --- a/sysgen/sysgen.go +++ /dev/null @@ -1,887 +0,0 @@ -// Copyright 2015/2016 syzkaller project authors. All rights reserved. -// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. - -package main - -import ( - "bufio" - "bytes" - "flag" - "fmt" - "go/format" - "io" - "io/ioutil" - "os" - "path/filepath" - "regexp" - "runtime" - "runtime/pprof" - "sort" - "strconv" - "strings" - - . "github.com/google/syzkaller/sys/sysparser" -) - -var ( - flagV = flag.Int("v", 0, "verbosity") - flagMemProfile = flag.String("memprofile", "", "write a memory profile to the file") - - intRegExp = regexp.MustCompile("^int([0-9]+|ptr)(be)?(:[0-9]+)?$") -) - -const ( - ptrSize = 8 -) - -func main() { - flag.Parse() - - inputFiles, err := filepath.Glob("sys/*\\.txt") - if err != nil { - failf("failed to find input files: %v", err) - } - var r io.Reader = bytes.NewReader(nil) - for _, f := range inputFiles { - inf, err := os.Open(f) - logf(1, "Load descriptions from file %v", f) - if err != nil { - failf("failed to open input file: %v", err) - } - defer inf.Close() - r = io.MultiReader(r, bufio.NewReader(inf)) - } - - logf(1, "Parse system call descriptions") - desc := Parse(r) - - consts := make(map[string]map[string]uint64) - for _, arch := range archs { - logf(0, "generating %v...", arch.Name) - consts[arch.Name] = readConsts(arch.Name) - - unsupported := make(map[string]bool) - archFlags := make(map[string][]string) - for f, vals := range desc.Flags { - var archVals []string - for _, val := range vals { - if isIdentifier(val) { - if v, ok := consts[arch.Name][val]; ok { - archVals = append(archVals, fmt.Sprint(v)) - } else { - if !unsupported[val] { - unsupported[val] = true - logf(0, "unsupported flag: %v", val) - } - } - } else { - archVals = append(archVals, val) - } - } - archFlags[f] = archVals - } - - sysFile := filepath.Join("sys", "sys_"+arch.Name+".go") - logf(1, "Generate code to init system call data in %v", sysFile) - out := new(bytes.Buffer) - archDesc := *desc - archDesc.Flags = archFlags - generate(arch.Name, &archDesc, consts[arch.Name], out) - writeSource(sysFile, out.Bytes()) - logf(0, "") - } - - generateExecutorSyscalls(desc.Syscalls, consts) - - if *flagMemProfile != "" { - f, err := os.Create(*flagMemProfile) - if err != nil { - failf("could not create memory profile: ", err) - } - runtime.GC() // get up-to-date statistics - if err := pprof.WriteHeapProfile(f); err != nil { - failf("could not write memory profile: ", err) - } - f.Close() - } -} - -func readConsts(arch string) map[string]uint64 { - constFiles, err := filepath.Glob("sys/*_" + arch + ".const") - if err != nil { - failf("failed to find const files: %v", err) - } - consts := make(map[string]uint64) - for _, fname := range constFiles { - f, err := os.Open(fname) - if err != nil { - failf("failed to open const file: %v", err) - } - defer f.Close() - s := bufio.NewScanner(f) - for s.Scan() { - line := s.Text() - if line == "" || line[0] == '#' { - continue - } - eq := strings.IndexByte(line, '=') - if eq == -1 { - failf("malformed const file %v: no '=' in '%v'", fname, line) - } - name := strings.TrimSpace(line[:eq]) - val, err := strconv.ParseUint(strings.TrimSpace(line[eq+1:]), 0, 64) - if err != nil { - failf("malformed const file %v: bad value in '%v'", fname, line) - } - if old, ok := consts[name]; ok && old != val { - failf("const %v has different values for %v: %v vs %v", name, arch, old, val) - } - consts[name] = val - } - if err := s.Err(); err != nil { - failf("failed to read const file: %v", err) - } - } - for name, nr := range syzkalls { - consts["__NR_"+name] = nr - } - return consts -} - -var skipCurrentSyscall string - -func skipSyscall(why string) { - if skipCurrentSyscall != "" { - skipCurrentSyscall = why - } -} - -func generate(arch string, desc *Description, consts map[string]uint64, out io.Writer) { - unsupported := make(map[string]bool) - - fmt.Fprintf(out, "// AUTOGENERATED FILE\n") - fmt.Fprintf(out, "package sys\n\n") - - generateResources(desc, consts, out) - generateStructs(desc, consts, out) - - fmt.Fprintf(out, "var Calls = []*Call{\n") - for _, s := range desc.Syscalls { - logf(4, " generate population code for %v", s.Name) - skipCurrentSyscall = "" - syscallNR := -1 - if nr, ok := consts["__NR_"+s.CallName]; ok { - syscallNR = int(nr) - } else { - if !unsupported[s.CallName] { - unsupported[s.CallName] = true - logf(0, "unsupported syscall: %v", s.CallName) - } - } - native := true - if _, ok := syzkalls[s.CallName]; ok { - native = false - } - fmt.Fprintf(out, "&Call{Name: \"%v\", CallName: \"%v\", Native: %v", s.Name, s.CallName, native) - if len(s.Ret) != 0 { - fmt.Fprintf(out, ", Ret: ") - generateArg("", "ret", s.Ret[0], "out", s.Ret[1:], desc, consts, true, false, out) - } - fmt.Fprintf(out, ", Args: []Type{") - for i, a := range s.Args { - if i != 0 { - fmt.Fprintf(out, ", ") - } - logf(5, " generate description for arg %v", i) - generateArg("", a[0], a[1], "in", a[2:], desc, consts, true, false, out) - } - if skipCurrentSyscall != "" { - logf(0, "unsupported syscall: %v due to %v", s.Name, skipCurrentSyscall) - syscallNR = -1 - } - fmt.Fprintf(out, "}, NR: %v},\n", syscallNR) - } - fmt.Fprintf(out, "}\n\n") - - var constArr []NameValue - for name, val := range consts { - constArr = append(constArr, NameValue{name, val}) - } - sort.Sort(NameValueArray(constArr)) - - fmt.Fprintf(out, "const (\n") - for _, nv := range constArr { - fmt.Fprintf(out, "%v = %v\n", nv.name, nv.val) - } - fmt.Fprintf(out, ")\n") -} - -func generateResources(desc *Description, consts map[string]uint64, out io.Writer) { - var resArray ResourceArray - for _, res := range desc.Resources { - resArray = append(resArray, res) - } - sort.Sort(resArray) - - fmt.Fprintf(out, "var resourceArray = []*ResourceDesc{\n") - for _, res := range resArray { - underlying := "" - name := res.Name - kind := []string{name} - var values []string - loop: - for { - var values1 []string - for _, v := range res.Values { - if v1, ok := consts[v]; ok { - values1 = append(values1, fmt.Sprint(v1)) - } else if !isIdentifier(v) { - values1 = append(values1, v) - } - } - values = append(values1, values...) - switch res.Base { - case "int8", "int16", "int32", "int64", "intptr": - underlying = res.Base - break loop - default: - if _, ok := desc.Resources[res.Base]; !ok { - failf("resource '%v' has unknown parent resource '%v'", name, res.Base) - } - kind = append([]string{res.Base}, kind...) - res = desc.Resources[res.Base] - } - } - fmt.Fprintf(out, "&ResourceDesc{Name: \"%v\", Type: ", name) - generateArg("", "resource-type", underlying, "inout", nil, desc, consts, true, true, out) - fmt.Fprintf(out, ", Kind: []string{") - for i, k := range kind { - if i != 0 { - fmt.Fprintf(out, ", ") - } - fmt.Fprintf(out, "\"%v\"", k) - } - fmt.Fprintf(out, "}, Values: []uintptr{") - if len(values) == 0 { - values = append(values, "0") - } - for i, v := range values { - if i != 0 { - fmt.Fprintf(out, ", ") - } - fmt.Fprintf(out, "%v", v) - } - fmt.Fprintf(out, "}},\n") - } - fmt.Fprintf(out, "}\n") -} - -type structKey struct { - name string - field string - dir string -} - -func generateStructEntry(str *Struct, out io.Writer) { - typ := "StructType" - if str.IsUnion { - typ = "UnionType" - } - packed := "" - if str.Packed { - packed = ", packed: true" - } - varlen := "" - if str.Varlen { - varlen = ", varlen: true" - } - align := "" - if str.Align != 0 { - align = fmt.Sprintf(", align: %v", str.Align) - } - fmt.Fprintf(out, "&%v{TypeCommon: TypeCommon{TypeName: \"%v\", IsOptional: %v} %v %v %v},\n", - typ, str.Name, false, packed, align, varlen) -} - -func generateStructFields(str *Struct, key structKey, desc *Description, consts map[string]uint64, out io.Writer) { - fmt.Fprintf(out, "{structKey{\"%v\", \"%v\", %v}, []Type{\n", key.name, key.field, fmtDir(key.dir)) - for _, a := range str.Flds { - generateArg(str.Name, a[0], a[1], key.dir, a[2:], desc, consts, false, true, out) - fmt.Fprintf(out, ",\n") - } - fmt.Fprintf(out, "}},\n") - -} - -func generateStructs(desc *Description, consts map[string]uint64, out io.Writer) { - // Struct fields can refer to other structs. Go compiler won't like if - // we refer to Structs during Structs initialization. So we do - // it in 2 passes: on the first pass create struct types without fields, - // on the second pass we fill in fields. - - // Since structs of the same type can be fields with different names - // of multiple other structs, we have an instance of those structs - // for each field indexed by the name of the parent struct, field name and dir. - - structMap := make(map[structKey]*Struct) - for _, str := range desc.Structs { - for _, dir := range []string{"in", "out", "inout"} { - structMap[structKey{str.Name, "", dir}] = str - } - for _, a := range str.Flds { - if innerStr, ok := desc.Structs[a[1]]; ok { - for _, dir := range []string{"in", "out", "inout"} { - structMap[structKey{a[1], a[0], dir}] = innerStr - } - } - } - } - - fmt.Fprintf(out, "var structArray = []Type{\n") - sortedStructs := make([]*Struct, 0, len(desc.Structs)) - for _, str := range desc.Structs { - sortedStructs = append(sortedStructs, str) - } - sort.Sort(structSorter(sortedStructs)) - for _, str := range sortedStructs { - generateStructEntry(str, out) - } - fmt.Fprintf(out, "}\n") - - fmt.Fprintf(out, "var structFields = []struct{key structKey; fields []Type}{") - sortedKeys := make([]structKey, 0, len(structMap)) - for key := range structMap { - sortedKeys = append(sortedKeys, key) - } - sort.Sort(structKeySorter(sortedKeys)) - for _, key := range sortedKeys { - generateStructFields(structMap[key], key, desc, consts, out) - } - fmt.Fprintf(out, "}\n") -} - -func parseRange(buffer string, consts map[string]uint64) (string, string) { - lookupConst := func(name string) string { - if v, ok := consts[name]; ok { - return fmt.Sprint(v) - } - return name - } - - parts := strings.Split(buffer, ":") - switch len(parts) { - case 1: - v := lookupConst(buffer) - return v, v - case 2: - return lookupConst(parts[0]), lookupConst(parts[1]) - default: - failf("bad range: %v", buffer) - return "", "" - } -} - -func generateArg( - parent, name, typ, dir string, - a []string, - desc *Description, - consts map[string]uint64, - isArg, isField bool, - out io.Writer) { - origName := name - name = "\"" + name + "\"" - opt := false - for i, v := range a { - if v == "opt" { - opt = true - copy(a[i:], a[i+1:]) - a = a[:len(a)-1] - break - } - } - fmtDir(dir) // Make sure that dir is "in", "out" or "inout" - common := func() string { - return fmt.Sprintf("TypeCommon: TypeCommon{TypeName: \"%v\", FldName: %v, ArgDir: %v, IsOptional: %v}", typ, name, fmtDir(dir), opt) - } - intCommon := func(typeSize uint64, bigEndian bool, bitfieldLen uint64) string { - // BitfieldOff and BitfieldLst will be filled in in initAlign(). - return fmt.Sprintf("IntTypeCommon: IntTypeCommon{%v, TypeSize: %v, BigEndian: %v, BitfieldLen: %v}", common(), typeSize, bigEndian, bitfieldLen) - } - canBeArg := false - switch typ { - case "fileoff": - canBeArg = true - size := uint64(ptrSize) - bigEndian := false - bitfieldLen := uint64(0) - if isField { - if want := 1; len(a) != want { - failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) - } - size, bigEndian, bitfieldLen = decodeIntType(a[0]) - } else { - if want := 0; len(a) != want { - failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) - } - } - fmt.Fprintf(out, "&IntType{%v, Kind: IntFileoff}", intCommon(size, bigEndian, bitfieldLen)) - case "buffer": - canBeArg = true - if want := 1; len(a) != want { - failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) - } - ptrCommonHdr := common() - dir = a[0] - opt = false - fmt.Fprintf(out, "&PtrType{%v, Type: &BufferType{%v, Kind: BufferBlobRand}}", ptrCommonHdr, common()) - case "string": - if len(a) != 0 && len(a) != 1 && len(a) != 2 { - failf("wrong number of arguments for %v arg %v, want 0-2, got %v", typ, name, len(a)) - } - var vals []string - subkind := "" - if len(a) >= 1 { - if a[0][0] == '"' { - vals = append(vals, a[0][1:len(a[0])-1]) - } else { - vals1, ok := desc.StrFlags[a[0]] - if !ok { - failf("unknown string flags %v", a[0]) - } - vals = append([]string{}, vals1...) - subkind = a[0] - } - } - for i, s := range vals { - vals[i] = s + "\x00" - } - var size uint64 - if len(a) >= 2 { - if v, ok := consts[a[1]]; ok { - size = v - } else { - v, err := strconv.ParseUint(a[1], 10, 64) - if err != nil { - failf("failed to parse string length for %v", name, a[1]) - } - size = v - } - for i, s := range vals { - if uint64(len(s)) > size { - failf("string value %q exceeds buffer length %v for arg %v", s, size, name) - } - for uint64(len(s)) < size { - s += "\x00" - } - vals[i] = s - } - } else { - for _, s := range vals { - if size != 0 && size != uint64(len(s)) { - size = 0 - break - } - size = uint64(len(s)) - } - } - fmt.Fprintf(out, "&BufferType{%v, Kind: BufferString, SubKind: %q, Values: %#v, Length: %v}", common(), subkind, vals, size) - case "salg_type": - if want := 0; len(a) != want { - failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) - } - fmt.Fprintf(out, "&BufferType{%v, Kind: BufferAlgType}", common()) - case "salg_name": - if want := 0; len(a) != want { - failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) - } - fmt.Fprintf(out, "&BufferType{%v, Kind: BufferAlgName}", common()) - case "vma": - canBeArg = true - begin, end := "0", "0" - switch len(a) { - case 0: - case 1: - begin, end = parseRange(a[0], consts) - default: - failf("wrong number of arguments for %v arg %v, want 0 or 1, got %v", typ, name, len(a)) - } - fmt.Fprintf(out, "&VmaType{%v, RangeBegin: %v, RangeEnd: %v}", common(), begin, end) - case "len", "bytesize", "bytesize2", "bytesize4", "bytesize8": - canBeArg = true - size := uint64(ptrSize) - bigEndian := false - bitfieldLen := uint64(0) - if isField { - if want := 2; len(a) != want { - failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) - } - size, bigEndian, bitfieldLen = decodeIntType(a[1]) - } else { - if want := 1; len(a) != want { - failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) - } - } - byteSize := uint8(0) - if typ != "len" { - byteSize = decodeByteSizeType(typ) - } - fmt.Fprintf(out, "&LenType{%v, Buf: \"%v\", ByteSize: %v}", intCommon(size, bigEndian, bitfieldLen), a[0], byteSize) - case "csum": - if len(a) != 3 && len(a) != 4 { - failf("wrong number of arguments for %v arg %v, want 3-4, got %v", typ, name, len(a)) - } - var size uint64 - var bigEndian bool - var bitfieldLen uint64 - var protocol uint64 - var kind string - switch a[1] { - case "inet": - kind = "CsumInet" - size, bigEndian, bitfieldLen = decodeIntType(a[2]) - case "pseudo": - kind = "CsumPseudo" - size, bigEndian, bitfieldLen = decodeIntType(a[3]) - if v, ok := consts[a[2]]; ok { - protocol = v - } else { - v, err := strconv.ParseUint(a[2], 10, 64) - if err != nil { - failf("failed to parse protocol %v", a[2]) - } - protocol = v - } - default: - failf("unknown checksum kind '%v'", a[0]) - } - fmt.Fprintf(out, "&CsumType{%v, Buf: \"%s\", Kind: %v, Protocol: %v}", intCommon(size, bigEndian, bitfieldLen), a[0], kind, protocol) - case "flags": - canBeArg = true - size := uint64(ptrSize) - bigEndian := false - bitfieldLen := uint64(0) - if isField { - if want := 2; len(a) != want { - failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) - } - size, bigEndian, bitfieldLen = decodeIntType(a[1]) - } else { - if want := 1; len(a) != want { - failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) - } - } - vals, ok := desc.Flags[a[0]] - if !ok { - failf("unknown flag %v", a[0]) - } - if len(vals) == 0 { - fmt.Fprintf(out, "&IntType{%v}", intCommon(size, bigEndian, bitfieldLen)) - } else { - fmt.Fprintf(out, "&FlagsType{%v, Vals: []uintptr{%v}}", intCommon(size, bigEndian, bitfieldLen), strings.Join(vals, ",")) - } - case "const": - canBeArg = true - size := uint64(ptrSize) - bigEndian := false - bitfieldLen := uint64(0) - if isField { - if want := 2; len(a) != want { - failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) - } - size, bigEndian, bitfieldLen = decodeIntType(a[1]) - } else { - if want := 1; len(a) != want { - failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) - } - } - val := a[0] - if v, ok := consts[a[0]]; ok { - val = fmt.Sprint(v) - } else if isIdentifier(a[0]) { - // This is an identifier for which we don't have a value for this arch. - // Skip this syscall on this arch. - val = "0" - skipSyscall(fmt.Sprintf("missing const %v", a[0])) - } - fmt.Fprintf(out, "&ConstType{%v, Val: uintptr(%v)}", intCommon(size, bigEndian, bitfieldLen), val) - case "proc": - canBeArg = true - size := uint64(ptrSize) - bigEndian := false - bitfieldLen := uint64(0) - var valuesStart string - var valuesPerProc string - if isField { - if want := 3; len(a) != want { - failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) - } - size, bigEndian, bitfieldLen = decodeIntType(a[0]) - valuesStart = a[1] - valuesPerProc = a[2] - } else { - if want := 2; len(a) != want { - failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) - } - valuesStart = a[0] - valuesPerProc = a[1] - } - valuesStartInt, err := strconv.ParseInt(valuesStart, 10, 64) - if err != nil { - failf("couldn't parse '%v' as int64", valuesStart) - } - valuesPerProcInt, err := strconv.ParseInt(valuesPerProc, 10, 64) - if err != nil { - failf("couldn't parse '%v' as int64", valuesPerProc) - } - if valuesPerProcInt < 1 { - failf("values per proc '%v' should be >= 1", valuesPerProcInt) - } - if size != 8 && valuesStartInt >= (1<<(size*8)) { - failf("values starting from '%v' overflow desired type of size '%v'", valuesStartInt, size) - } - const maxPids = 32 // executor knows about this constant (MAX_PIDS) - if size != 8 && valuesStartInt+maxPids*valuesPerProcInt >= (1<<(size*8)) { - failf("not enough values starting from '%v' with step '%v' and type size '%v' for 32 procs", valuesStartInt, valuesPerProcInt, size) - } - fmt.Fprintf(out, "&ProcType{%v, ValuesStart: %v, ValuesPerProc: %v}", intCommon(size, bigEndian, bitfieldLen), valuesStartInt, valuesPerProcInt) - case "signalno": - canBeArg = true - if want := 0; len(a) != want { - failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) - } - fmt.Fprintf(out, "&IntType{%v, Kind: IntSignalno}", intCommon(4, false, 0)) - case "filename": - if want := 0; len(a) != want { - failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) - } - fmt.Fprintf(out, "&BufferType{%v, Kind: BufferFilename}", common()) - case "text": - if want := 1; len(a) != want { - failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) - } - kind := "" - switch a[0] { - case "x86_real", "x86_16", "x86_32", "x86_64", "arm64": - kind = "Text_" + a[0] - default: - failf("unknown text type %v for %v arg %v", a[0], typ, name) - } - fmt.Fprintf(out, "&BufferType{%v, Kind: BufferText, Text: %v}", common(), kind) - case "array": - if len(a) != 1 && len(a) != 2 { - failf("wrong number of arguments for %v arg %v, want 1 or 2, got %v", typ, name, len(a)) - } - if len(a) == 1 { - if a[0] == "int8" { - fmt.Fprintf(out, "&BufferType{%v, Kind: BufferBlobRand}", common()) - } else { - fmt.Fprintf(out, "&ArrayType{%v, Type: %v, Kind: ArrayRandLen}", common(), generateType(a[0], dir, desc, consts)) - } - } else { - begin, end := parseRange(a[1], consts) - if a[0] == "int8" { - fmt.Fprintf(out, "&BufferType{%v, Kind: BufferBlobRange, RangeBegin: %v, RangeEnd: %v}", common(), begin, end) - } else { - fmt.Fprintf(out, "&ArrayType{%v, Type: %v, Kind: ArrayRangeLen, RangeBegin: %v, RangeEnd: %v}", common(), generateType(a[0], dir, desc, consts), begin, end) - } - } - case "ptr": - canBeArg = true - if want := 2; len(a) != want { - failf("wrong number of arguments for %v arg %v, want %v, got %v", typ, name, want, len(a)) - } - dir = "in" - fmt.Fprintf(out, "&PtrType{%v, Type: %v}", common(), generateType(a[1], a[0], desc, consts)) - default: - if intRegExp.MatchString(typ) { - canBeArg = true - size, bigEndian, bitfieldLen := decodeIntType(typ) - switch len(a) { - case 0: - fmt.Fprintf(out, "&IntType{%v}", intCommon(size, bigEndian, bitfieldLen)) - case 1: - begin, end := parseRange(a[0], consts) - fmt.Fprintf(out, "&IntType{%v, Kind: IntRange, RangeBegin: %v, RangeEnd: %v}", - intCommon(size, bigEndian, bitfieldLen), begin, end) - default: - failf("wrong number of arguments for %v arg %v, want 0 or 1, got %v", typ, name, len(a)) - } - } else if strings.HasPrefix(typ, "unnamed") { - if inner, ok := desc.Unnamed[typ]; ok { - generateArg("", "", inner[0], dir, inner[1:], desc, consts, false, isField, out) - } else { - failf("unknown unnamed type '%v'", typ) - } - } else if _, ok := desc.Structs[typ]; ok { - if len(a) != 0 { - failf("struct '%v' has args", typ) - } - fmt.Fprintf(out, "getStruct(structKey{\"%v\", \"%v\", %v})", typ, origName, fmtDir(dir)) - } else if _, ok := desc.Resources[typ]; ok { - if len(a) != 0 { - failf("resource '%v' has args", typ) - } - fmt.Fprintf(out, "&ResourceType{%v, Desc: resource(\"%v\")}", common(), typ) - return - } else { - failf("unknown arg type \"%v\" for %v", typ, name) - } - } - if isArg && !canBeArg { - failf("%v %v can't be syscall argument/return", name, typ) - } -} - -func generateType(typ, dir string, desc *Description, consts map[string]uint64) string { - buf := new(bytes.Buffer) - generateArg("", "", typ, dir, nil, desc, consts, false, true, buf) - return buf.String() -} - -func fmtDir(s string) string { - switch s { - case "in": - return "DirIn" - case "out": - return "DirOut" - case "inout": - return "DirInOut" - default: - failf("bad direction %v", s) - return "" - } -} - -func decodeIntType(typ string) (uint64, bool, uint64) { - bigEndian := false - bitfieldLen := uint64(0) - - parts := strings.Split(typ, ":") - if len(parts) == 2 { - var err error - bitfieldLen, err = strconv.ParseUint(parts[1], 10, 64) - if err != nil { - failf("failed to parse bitfield length '%v'", parts[1]) - } - typ = parts[0] - } - - if strings.HasSuffix(typ, "be") { - bigEndian = true - typ = typ[:len(typ)-2] - } - - switch typ { - case "int8", "int16", "int32", "int64", "intptr": - default: - failf("unknown type %v", typ) - } - sz := int64(ptrSize * 8) - if typ != "intptr" { - sz, _ = strconv.ParseInt(typ[3:], 10, 64) - } - - if bitfieldLen > uint64(sz) { - failf("bitfield of size %v is too large for base type of size %v", bitfieldLen, sz/8) - } - - return uint64(sz / 8), bigEndian, bitfieldLen -} - -func decodeByteSizeType(typ string) uint8 { - switch typ { - case "bytesize", "bytesize2", "bytesize4", "bytesize8": - default: - failf("unknown type %v", typ) - } - sz := int64(1) - if typ != "bytesize" { - sz, _ = strconv.ParseInt(typ[8:], 10, 8) - } - return uint8(sz) -} - -func isIdentifier(s string) bool { - for i, c := range s { - if c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || i > 0 && (c >= '0' && c <= '9') { - continue - } - return false - } - return true -} - -func writeSource(file string, data []byte) { - src, err := format.Source(data) - if err != nil { - fmt.Printf("%s\n", data) - failf("failed to format output: %v", err) - } - if oldSrc, err := ioutil.ReadFile(file); err == nil && bytes.Equal(src, oldSrc) { - return - } - writeFile(file, src) -} - -func writeFile(file string, data []byte) { - outf, err := os.Create(file) - if err != nil { - failf("failed to create output file: %v", err) - } - defer outf.Close() - outf.Write(data) -} - -type NameValue struct { - name string - val uint64 -} - -type NameValueArray []NameValue - -func (a NameValueArray) Len() int { return len(a) } -func (a NameValueArray) Less(i, j int) bool { return a[i].name < a[j].name } -func (a NameValueArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -type ResourceArray []Resource - -func (a ResourceArray) Len() int { return len(a) } -func (a ResourceArray) Less(i, j int) bool { return a[i].Name < a[j].Name } -func (a ResourceArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -type structSorter []*Struct - -func (a structSorter) Len() int { return len(a) } -func (a structSorter) Less(i, j int) bool { return a[i].Name < a[j].Name } -func (a structSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -type structKeySorter []structKey - -func (a structKeySorter) Len() int { return len(a) } -func (a structKeySorter) Less(i, j int) bool { - if a[i].name < a[j].name { - return true - } - if a[i].name > a[j].name { - return false - } - if a[i].field < a[j].field { - return true - } - if a[i].field > a[j].field { - return false - } - return a[i].dir < a[j].dir -} -func (a structKeySorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -func failf(msg string, args ...interface{}) { - fmt.Fprintf(os.Stderr, msg+"\n", args...) - os.Exit(1) -} - -func logf(v int, msg string, args ...interface{}) { - if *flagV >= v { - fmt.Fprintf(os.Stderr, msg+"\n", args...) - } -} -- cgit mrf-deployment