From 8094a4202f10d59457a2a33d4e276b32c0ca16bf Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Mon, 4 Sep 2017 19:52:42 +0200 Subject: [PATCH] pkg/compiler: move checking code to a separate file --- pkg/compiler/check.go | 403 +++++++++++++++++++++++++++++++++++++++ pkg/compiler/compiler.go | 390 ------------------------------------- 2 files changed, 403 insertions(+), 390 deletions(-) create mode 100644 pkg/compiler/check.go diff --git a/pkg/compiler/check.go b/pkg/compiler/check.go new file mode 100644 index 00000000..d6db087c --- /dev/null +++ b/pkg/compiler/check.go @@ -0,0 +1,403 @@ +// Copyright 2017 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 compiler generates sys descriptions of syscalls, types and resources +// from textual descriptions. +package compiler + +import ( + "fmt" + + "github.com/google/syzkaller/pkg/ast" + "github.com/google/syzkaller/sys" +) + +func (comp *compiler) check() { + // TODO: check len in syscall arguments referring to parent. + // TODO: incorrect name is referenced in len type + // TODO: no constructor for a resource + + comp.checkNames() + comp.checkFields() + comp.checkTypes() + comp.checkRecursion() +} + +func (comp *compiler) checkNames() { + calls := make(map[string]*ast.Call) + for _, decl := range comp.desc.Nodes { + switch decl.(type) { + case *ast.Resource, *ast.Struct: + pos, typ, name := decl.Info() + if builtinTypes[name] != nil { + comp.error(pos, "%v name %v conflicts with builtin type", typ, name) + continue + } + if prev := comp.resources[name]; prev != nil { + comp.error(pos, "type %v redeclared, previously declared as resource at %v", + name, prev.Pos) + continue + } + if prev := comp.structs[name]; prev != nil { + _, typ, _ := prev.Info() + comp.error(pos, "type %v redeclared, previously declared as %v at %v", + name, typ, prev.Pos) + continue + } + if res, ok := decl.(*ast.Resource); ok { + comp.resources[name] = res + } else if str, ok := decl.(*ast.Struct); ok { + comp.structs[name] = str + } + case *ast.IntFlags: + n := decl.(*ast.IntFlags) + name := n.Name.Name + if prev := comp.intFlags[name]; prev != nil { + comp.error(n.Pos, "flags %v redeclared, previously declared at %v", + name, prev.Pos) + continue + } + comp.intFlags[name] = n + case *ast.StrFlags: + n := decl.(*ast.StrFlags) + name := n.Name.Name + if prev := comp.strFlags[name]; prev != nil { + comp.error(n.Pos, "string flags %v redeclared, previously declared at %v", + name, prev.Pos) + continue + } + comp.strFlags[name] = n + case *ast.Call: + c := decl.(*ast.Call) + name := c.Name.Name + if prev := calls[name]; prev != nil { + comp.error(c.Pos, "syscall %v redeclared, previously declared at %v", + name, prev.Pos) + } + calls[name] = c + } + } +} + +func (comp *compiler) checkFields() { + const maxArgs = 9 // executor does not support more + for _, decl := range comp.desc.Nodes { + switch n := decl.(type) { + case *ast.Struct: + _, typ, name := n.Info() + fields := make(map[string]bool) + for _, f := range n.Fields { + fn := f.Name.Name + if fn == "parent" { + comp.error(f.Pos, "reserved field name %v in %v %v", fn, typ, name) + } + if fields[fn] { + comp.error(f.Pos, "duplicate field %v in %v %v", fn, typ, name) + } + fields[fn] = true + } + if !n.IsUnion && len(n.Fields) < 1 { + comp.error(n.Pos, "struct %v has no fields, need at least 1 field", name) + } + if n.IsUnion && len(n.Fields) < 2 { + comp.error(n.Pos, "union %v has only %v field, need at least 2 fields", + name, len(n.Fields)) + } + case *ast.Call: + name := n.Name.Name + args := make(map[string]bool) + for _, a := range n.Args { + an := a.Name.Name + if an == "parent" { + comp.error(a.Pos, "reserved argument name %v in syscall %v", + an, name) + } + if args[an] { + comp.error(a.Pos, "duplicate argument %v in syscall %v", + an, name) + } + args[an] = true + } + if len(n.Args) > maxArgs { + comp.error(n.Pos, "syscall %v has %v arguments, allowed maximum is %v", + name, len(n.Args), maxArgs) + } + } + } +} + +func (comp *compiler) checkTypes() { + for _, decl := range comp.desc.Nodes { + switch n := decl.(type) { + case *ast.Resource: + comp.checkType(n.Base, false, true) + case *ast.Struct: + for _, f := range n.Fields { + comp.checkType(f.Type, false, false) + } + comp.checkStruct(n) + case *ast.Call: + for _, a := range n.Args { + comp.checkType(a.Type, true, false) + } + if n.Ret != nil { + comp.checkType(n.Ret, true, false) + } + } + } +} + +func (comp *compiler) checkRecursion() { + checked := make(map[string]bool) + for _, decl := range comp.desc.Nodes { + switch n := decl.(type) { + case *ast.Resource: + comp.checkResourceRecursion(n) + case *ast.Struct: + comp.checkStructRecursion(checked, n) + } + } +} + +func (comp *compiler) checkResourceRecursion(n *ast.Resource) { + var seen []string + for n != nil { + if arrayContains(seen, n.Name.Name) { + chain := "" + for _, r := range seen { + chain += r + "->" + } + chain += n.Name.Name + comp.error(n.Pos, "recursive resource %v", chain) + return + } + seen = append(seen, n.Name.Name) + n = comp.resources[n.Base.Ident] + } +} + +type pathElem struct { + Pos ast.Pos + Struct string + Field string +} + +func (comp *compiler) checkStructRecursion(checked map[string]bool, n *ast.Struct) { + var path []pathElem + comp.checkStructRecursion1(checked, n, path) +} + +func (comp *compiler) checkStructRecursion1(checked map[string]bool, n *ast.Struct, path []pathElem) { + name := n.Name.Name + if checked[name] { + return + } + for i, elem := range path { + if elem.Struct != name { + continue + } + path = path[i:] + str := "" + for _, elem := range path { + str += fmt.Sprintf("%v.%v -> ", elem.Struct, elem.Field) + } + str += name + comp.error(path[0].Pos, "recursive declaration: %v (mark some pointers as opt)", str) + checked[name] = true + return + } + for _, f := range n.Fields { + path = append(path, pathElem{ + Pos: f.Pos, + Struct: name, + Field: f.Name.Name, + }) + comp.recurseField(checked, f.Type, path) + path = path[:len(path)-1] + } + checked[name] = true +} + +func (comp *compiler) recurseField(checked map[string]bool, t *ast.Type, path []pathElem) { + desc := comp.getTypeDesc(t) + if desc == typeStruct { + comp.checkStructRecursion1(checked, comp.structs[t.Ident], path) + return + } + _, args, base := comp.getArgsBase(t, "", sys.DirIn, false) + if desc == typePtr && base.IsOptional { + return // optional pointers prune recursion + } + for i, arg := range args { + if desc.Args[i].Type == typeArgType { + comp.recurseField(checked, arg, path) + } + } +} + +func (comp *compiler) checkStruct(n *ast.Struct) { + if n.IsUnion { + comp.parseUnionAttrs(n) + } else { + comp.parseStructAttrs(n) + } +} + +func (comp *compiler) checkType(t *ast.Type, isArg, isResourceBase bool) { + if unexpected, _, ok := checkTypeKind(t, kindIdent); !ok { + comp.error(t.Pos, "unexpected %v, expect type", unexpected) + return + } + desc := comp.getTypeDesc(t) + if desc == nil { + comp.error(t.Pos, "unknown type %v", t.Ident) + return + } + if !desc.AllowColon && t.HasColon { + comp.error(t.Pos2, "unexpected ':'") + return + } + if isArg && !desc.CanBeArg { + comp.error(t.Pos, "%v can't be syscall argument/return", t.Ident) + return + } + if isResourceBase && !desc.ResourceBase { + comp.error(t.Pos, "%v can't be resource base (int types can)", t.Ident) + return + } + args, opt := removeOpt(t) + if opt && (desc.CantBeOpt || isResourceBase) { + what := "resource base" + if desc.CantBeOpt { + what = t.Ident + } + pos := t.Args[len(t.Args)-1].Pos + comp.error(pos, "%v can't be marked as opt", what) + return + } + addArgs := 0 + needBase := !isArg && desc.NeedBase + if needBase { + addArgs++ // last arg must be base type, e.g. const[0, int32] + } + if len(args) > len(desc.Args)+addArgs || len(args) < len(desc.Args)-desc.OptArgs+addArgs { + comp.error(t.Pos, "wrong number of arguments for type %v, expect %v", + t.Ident, expectedTypeArgs(desc, needBase)) + return + } + if needBase { + base := args[len(args)-1] + args = args[:len(args)-1] + comp.checkTypeArg(t, base, typeArgBase) + } + err0 := comp.errors + for i, arg := range args { + if desc.Args[i].Type == typeArgType { + comp.checkType(arg, false, false) + } else { + comp.checkTypeArg(t, arg, desc.Args[i]) + } + } + if err0 != comp.errors { + return + } + if desc.Check != nil { + _, args, base := comp.getArgsBase(t, "", sys.DirIn, isArg) + desc.Check(comp, t, args, base) + } +} + +func (comp *compiler) checkTypeArg(t, arg *ast.Type, argDesc namedArg) { + desc := argDesc.Type + if len(desc.Names) != 0 { + if unexpected, _, ok := checkTypeKind(arg, kindIdent); !ok { + comp.error(arg.Pos, "unexpected %v for %v argument of %v type, expect %+v", + unexpected, argDesc.Name, t.Ident, desc.Names) + return + } + if !arrayContains(desc.Names, arg.Ident) { + comp.error(arg.Pos, "unexpected value %v for %v argument of %v type, expect %+v", + arg.Ident, argDesc.Name, t.Ident, desc.Names) + return + } + } else { + if unexpected, expect, ok := checkTypeKind(arg, desc.Kind); !ok { + comp.error(arg.Pos, "unexpected %v for %v argument of %v type, expect %v", + unexpected, argDesc.Name, t.Ident, expect) + return + } + } + if !desc.AllowColon && arg.HasColon { + comp.error(arg.Pos2, "unexpected ':'") + return + } + if desc.Check != nil { + desc.Check(comp, arg) + } +} + +func expectedTypeArgs(desc *typeDesc, needBase bool) string { + expect := "" + for i, arg := range desc.Args { + if expect != "" { + expect += ", " + } + opt := i >= len(desc.Args)-desc.OptArgs + if opt { + expect += "[" + } + expect += arg.Name + if opt { + expect += "]" + } + } + if needBase { + if expect != "" { + expect += ", " + } + expect += typeArgBase.Name + } + if !desc.CantBeOpt { + if expect != "" { + expect += ", " + } + expect += "[opt]" + } + if expect == "" { + expect = "no arguments" + } + return expect +} + +func checkTypeKind(t *ast.Type, kind int) (unexpected string, expect string, ok bool) { + switch { + case kind == kindAny: + ok = true + case t.String != "": + ok = kind == kindString + if !ok { + unexpected = fmt.Sprintf("string %q", t.String) + } + case t.Ident != "": + ok = kind == kindIdent + if !ok { + unexpected = fmt.Sprintf("identifier %v", t.Ident) + } + default: + ok = kind == kindInt + if !ok { + unexpected = fmt.Sprintf("int %v", t.Value) + } + } + if !ok { + switch kind { + case kindString: + expect = "string" + case kindIdent: + expect = "identifier" + case kindInt: + expect = "int" + } + } + return +} diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index a192ed8d..fe3f28d7 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -105,237 +105,6 @@ func (comp *compiler) warning(pos ast.Pos, msg string, args ...interface{}) { comp.warnings = append(comp.warnings, warn{pos, fmt.Sprintf(msg, args...)}) } -func (comp *compiler) check() { - // TODO: check len in syscall arguments referring to parent. - // TODO: incorrect name is referenced in len type - // TODO: no constructor for a resource - - comp.checkNames() - comp.checkFields() - comp.checkTypes() - comp.checkRecursion() -} - -func (comp *compiler) checkNames() { - calls := make(map[string]*ast.Call) - for _, decl := range comp.desc.Nodes { - switch decl.(type) { - case *ast.Resource, *ast.Struct: - pos, typ, name := decl.Info() - if builtinTypes[name] != nil { - comp.error(pos, "%v name %v conflicts with builtin type", typ, name) - continue - } - if prev := comp.resources[name]; prev != nil { - comp.error(pos, "type %v redeclared, previously declared as resource at %v", - name, prev.Pos) - continue - } - if prev := comp.structs[name]; prev != nil { - _, typ, _ := prev.Info() - comp.error(pos, "type %v redeclared, previously declared as %v at %v", - name, typ, prev.Pos) - continue - } - if res, ok := decl.(*ast.Resource); ok { - comp.resources[name] = res - } else if str, ok := decl.(*ast.Struct); ok { - comp.structs[name] = str - } - case *ast.IntFlags: - n := decl.(*ast.IntFlags) - name := n.Name.Name - if prev := comp.intFlags[name]; prev != nil { - comp.error(n.Pos, "flags %v redeclared, previously declared at %v", - name, prev.Pos) - continue - } - comp.intFlags[name] = n - case *ast.StrFlags: - n := decl.(*ast.StrFlags) - name := n.Name.Name - if prev := comp.strFlags[name]; prev != nil { - comp.error(n.Pos, "string flags %v redeclared, previously declared at %v", - name, prev.Pos) - continue - } - comp.strFlags[name] = n - case *ast.Call: - c := decl.(*ast.Call) - name := c.Name.Name - if prev := calls[name]; prev != nil { - comp.error(c.Pos, "syscall %v redeclared, previously declared at %v", - name, prev.Pos) - } - calls[name] = c - } - } -} - -func (comp *compiler) checkFields() { - const maxArgs = 9 // executor does not support more - for _, decl := range comp.desc.Nodes { - switch n := decl.(type) { - case *ast.Struct: - _, typ, name := n.Info() - fields := make(map[string]bool) - for _, f := range n.Fields { - fn := f.Name.Name - if fn == "parent" { - comp.error(f.Pos, "reserved field name %v in %v %v", fn, typ, name) - } - if fields[fn] { - comp.error(f.Pos, "duplicate field %v in %v %v", fn, typ, name) - } - fields[fn] = true - } - if !n.IsUnion && len(n.Fields) < 1 { - comp.error(n.Pos, "struct %v has no fields, need at least 1 field", name) - } - if n.IsUnion && len(n.Fields) < 2 { - comp.error(n.Pos, "union %v has only %v field, need at least 2 fields", - name, len(n.Fields)) - } - case *ast.Call: - name := n.Name.Name - args := make(map[string]bool) - for _, a := range n.Args { - an := a.Name.Name - if an == "parent" { - comp.error(a.Pos, "reserved argument name %v in syscall %v", - an, name) - } - if args[an] { - comp.error(a.Pos, "duplicate argument %v in syscall %v", - an, name) - } - args[an] = true - } - if len(n.Args) > maxArgs { - comp.error(n.Pos, "syscall %v has %v arguments, allowed maximum is %v", - name, len(n.Args), maxArgs) - } - } - } -} - -func (comp *compiler) checkTypes() { - for _, decl := range comp.desc.Nodes { - switch n := decl.(type) { - case *ast.Resource: - comp.checkType(n.Base, false, true) - case *ast.Struct: - for _, f := range n.Fields { - comp.checkType(f.Type, false, false) - } - comp.checkStruct(n) - case *ast.Call: - for _, a := range n.Args { - comp.checkType(a.Type, true, false) - } - if n.Ret != nil { - comp.checkType(n.Ret, true, false) - } - } - } -} - -func (comp *compiler) checkRecursion() { - checked := make(map[string]bool) - for _, decl := range comp.desc.Nodes { - switch n := decl.(type) { - case *ast.Resource: - comp.checkResourceRecursion(n) - case *ast.Struct: - comp.checkStructRecursion(checked, n) - } - } -} - -func (comp *compiler) checkResourceRecursion(n *ast.Resource) { - var seen []string - for n != nil { - if arrayContains(seen, n.Name.Name) { - chain := "" - for _, r := range seen { - chain += r + "->" - } - chain += n.Name.Name - comp.error(n.Pos, "recursive resource %v", chain) - return - } - seen = append(seen, n.Name.Name) - n = comp.resources[n.Base.Ident] - } -} - -type pathElem struct { - Pos ast.Pos - Struct string - Field string -} - -func (comp *compiler) checkStructRecursion(checked map[string]bool, n *ast.Struct) { - var path []pathElem - comp.checkStructRecursion1(checked, n, path) -} - -func (comp *compiler) checkStructRecursion1(checked map[string]bool, n *ast.Struct, path []pathElem) { - name := n.Name.Name - if checked[name] { - return - } - for i, elem := range path { - if elem.Struct != name { - continue - } - path = path[i:] - str := "" - for _, elem := range path { - str += fmt.Sprintf("%v.%v -> ", elem.Struct, elem.Field) - } - str += name - comp.error(path[0].Pos, "recursive declaration: %v (mark some pointers as opt)", str) - checked[name] = true - return - } - for _, f := range n.Fields { - path = append(path, pathElem{ - Pos: f.Pos, - Struct: name, - Field: f.Name.Name, - }) - comp.recurseField(checked, f.Type, path) - path = path[:len(path)-1] - } - checked[name] = true -} - -func (comp *compiler) recurseField(checked map[string]bool, t *ast.Type, path []pathElem) { - desc := comp.getTypeDesc(t) - if desc == typeStruct { - comp.checkStructRecursion1(checked, comp.structs[t.Ident], path) - return - } - _, args, base := comp.getArgsBase(t, "", sys.DirIn, false) - if desc == typePtr && base.IsOptional { - return // optional pointers prune recursion - } - for i, arg := range args { - if desc.Args[i].Type == typeArgType { - comp.recurseField(checked, arg, path) - } - } -} - -func (comp *compiler) checkStruct(n *ast.Struct) { - if n.IsUnion { - comp.parseUnionAttrs(n) - } else { - comp.parseStructAttrs(n) - } -} - func (comp *compiler) parseUnionAttrs(n *ast.Struct) (varlen bool) { for _, attr := range n.Attrs { switch attr.Name { @@ -389,99 +158,6 @@ func (comp *compiler) getTypeDesc(t *ast.Type) *typeDesc { return nil } -func (comp *compiler) checkType(t *ast.Type, isArg, isResourceBase bool) { - if unexpected, _, ok := checkTypeKind(t, kindIdent); !ok { - comp.error(t.Pos, "unexpected %v, expect type", unexpected) - return - } - desc := comp.getTypeDesc(t) - if desc == nil { - comp.error(t.Pos, "unknown type %v", t.Ident) - return - } - if !desc.AllowColon && t.HasColon { - comp.error(t.Pos2, "unexpected ':'") - return - } - if isArg && !desc.CanBeArg { - comp.error(t.Pos, "%v can't be syscall argument/return", t.Ident) - return - } - if isResourceBase && !desc.ResourceBase { - comp.error(t.Pos, "%v can't be resource base (int types can)", t.Ident) - return - } - args, opt := removeOpt(t) - if opt && (desc.CantBeOpt || isResourceBase) { - what := "resource base" - if desc.CantBeOpt { - what = t.Ident - } - pos := t.Args[len(t.Args)-1].Pos - comp.error(pos, "%v can't be marked as opt", what) - return - } - addArgs := 0 - needBase := !isArg && desc.NeedBase - if needBase { - addArgs++ // last arg must be base type, e.g. const[0, int32] - } - if len(args) > len(desc.Args)+addArgs || len(args) < len(desc.Args)-desc.OptArgs+addArgs { - comp.error(t.Pos, "wrong number of arguments for type %v, expect %v", - t.Ident, expectedTypeArgs(desc, needBase)) - return - } - if needBase { - base := args[len(args)-1] - args = args[:len(args)-1] - comp.checkTypeArg(t, base, typeArgBase) - } - err0 := comp.errors - for i, arg := range args { - if desc.Args[i].Type == typeArgType { - comp.checkType(arg, false, false) - } else { - comp.checkTypeArg(t, arg, desc.Args[i]) - } - } - if err0 != comp.errors { - return - } - if desc.Check != nil { - _, args, base := comp.getArgsBase(t, "", sys.DirIn, isArg) - desc.Check(comp, t, args, base) - } -} - -func (comp *compiler) checkTypeArg(t, arg *ast.Type, argDesc namedArg) { - desc := argDesc.Type - if len(desc.Names) != 0 { - if unexpected, _, ok := checkTypeKind(arg, kindIdent); !ok { - comp.error(arg.Pos, "unexpected %v for %v argument of %v type, expect %+v", - unexpected, argDesc.Name, t.Ident, desc.Names) - return - } - if !arrayContains(desc.Names, arg.Ident) { - comp.error(arg.Pos, "unexpected value %v for %v argument of %v type, expect %+v", - arg.Ident, argDesc.Name, t.Ident, desc.Names) - return - } - } else { - if unexpected, expect, ok := checkTypeKind(arg, desc.Kind); !ok { - comp.error(arg.Pos, "unexpected %v for %v argument of %v type, expect %v", - unexpected, argDesc.Name, t.Ident, expect) - return - } - } - if !desc.AllowColon && arg.HasColon { - comp.error(arg.Pos2, "unexpected ':'") - return - } - if desc.Check != nil { - desc.Check(comp, arg) - } -} - func (comp *compiler) getArgsBase(t *ast.Type, field string, dir sys.Dir, isArg bool) ( *typeDesc, []*ast.Type, sys.IntTypeCommon) { desc := comp.getTypeDesc(t) @@ -496,72 +172,6 @@ func (comp *compiler) getArgsBase(t *ast.Type, field string, dir sys.Dir, isArg return desc, args, base } -func expectedTypeArgs(desc *typeDesc, needBase bool) string { - expect := "" - for i, arg := range desc.Args { - if expect != "" { - expect += ", " - } - opt := i >= len(desc.Args)-desc.OptArgs - if opt { - expect += "[" - } - expect += arg.Name - if opt { - expect += "]" - } - } - if needBase { - if expect != "" { - expect += ", " - } - expect += typeArgBase.Name - } - if !desc.CantBeOpt { - if expect != "" { - expect += ", " - } - expect += "[opt]" - } - if expect == "" { - expect = "no arguments" - } - return expect -} - -func checkTypeKind(t *ast.Type, kind int) (unexpected string, expect string, ok bool) { - switch { - case kind == kindAny: - ok = true - case t.String != "": - ok = kind == kindString - if !ok { - unexpected = fmt.Sprintf("string %q", t.String) - } - case t.Ident != "": - ok = kind == kindIdent - if !ok { - unexpected = fmt.Sprintf("identifier %v", t.Ident) - } - default: - ok = kind == kindInt - if !ok { - unexpected = fmt.Sprintf("int %v", t.Value) - } - } - if !ok { - switch kind { - case kindString: - expect = "string" - case kindIdent: - expect = "identifier" - case kindInt: - expect = "int" - } - } - return -} - func removeOpt(t *ast.Type) ([]*ast.Type, bool) { args := t.Args if len(args) != 0 && args[len(args)-1].Ident == "opt" {